Skip to content

TS2.0.0@beta Conflicting library definitions for 'node' found: Winston vs. Express #10250

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
igabesz opened this issue Aug 10, 2016 · 10 comments
Closed
Assignees
Labels
External Relates to another program, environment, or user action which we cannot control. @types Relates to working with .d.ts files (declaration/definition files) from DefinitelyTyped

Comments

@igabesz
Copy link

igabesz commented Aug 10, 2016

TypeScript Version: 2.0.0@beta

Similar issue to #9494: conflict in Node library definitions. Got the following error for tsc:

node_modules/@types/winston/index.d.ts(8,1): 
message TS4090: Conflicting library definitions for 'node' found at 
'<root>/node_modules/@types/winston/node_modules/@types/node/index.d.ts' and 
'<root>/node_modules/@types/node/index.d.ts'. 
Copy the correct file to the 'typings' folder to resolve this conflict.

TS file

import * as express from 'express';
import * as winston from 'winston';

Express uses `@types/node@6.0.*1, while Winston uses 4.?.?.
Note that the build fails if I import both of these modules within the same file. I don't need to use anything from them; these 2 lines make the build fail.

package.json dependencies

  "dependencies": {
    "@types/express": "^4.0.30",
    "@types/node": "^6.0.32",
    "@types/winston": "0.0.27",
    "express": "^4.14.0",
    "winston": "^2.2.0"
  },

In #9494 @mhegazy mentions using @types/express@4.0.28-alpha but that doesn't work in this case.

Expected behavior:
The build should work even if there are 2 imports in the same file, and the 2 imports use separate node definition versions.

Actual behavior:
See the error above.

@igabesz
Copy link
Author

igabesz commented Aug 11, 2016

Actually I found some more interesting stuff. I already wasted a day on build debugging this and I don't have a clear solution but a workaround. (Plus other nasty things like npm links...) Here's what I got anyway.

In a certain case the @types/node definitions became unavailable in some TS files depending whether I included or not included some certain other @types references through an installed module. The fun fact is that the declare var process and stuff were right there in node_modules/@types/node/index.d.ts but the tsc did not see the declarations. Not even in files without any import statements.

My workaround was referring to the @types/node/index.d.ts file explicitly in the tsconfig.json's include list ('../node_modules/@types/node/index.d.ts'). And ✨ magic ✨ it compiles successfully.

Nevertheless, the upper issue still exists.

@RyanCavanaugh RyanCavanaugh self-assigned this Aug 11, 2016
@blakeembrey
Copy link
Contributor

@RyanCavanaugh I thought the issue of environmental typings being dependencies was going to be fixed? I can move that discussion into the types publisher though.

@DanielRosenwasser DanielRosenwasser added this to the TypeScript 2.0.1 milestone Aug 15, 2016
@DanielRosenwasser DanielRosenwasser added @types Relates to working with .d.ts files (declaration/definition files) from DefinitelyTyped Bug A bug in TypeScript labels Aug 15, 2016
@RyanCavanaugh RyanCavanaugh added External Relates to another program, environment, or user action which we cannot control. and removed Bug A bug in TypeScript labels Aug 15, 2016
@RyanCavanaugh
Copy link
Member

The desired fix is the one @igabesz did -- npm install @types/the-package@the-version-you-actually-want. When two packages depend on different copies of the same global setup, the user has to decide which one wins.

An underlying problem is that we shouldn't be publishing packages pointing to arbitrary versions of node -- they should all "agree" unless there's a reason not to. If there's a comprehensive solution that would solve this more elegantly, we should think about it. I'm not totally convinced about "environmental" since there are going to be e.g. UMD libraries that refer to some random types out of node that the user might not expect to have to include as part of their "environment".

@igabesz
Copy link
Author

igabesz commented Aug 15, 2016

Just to sum it up. Starting from an empty directory:

npm i @types/express @types/winston

app.ts

import * as express from 'express';
import * as winston from 'winston';

tsconfig.json

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs"
    }
}

And now the build: tsc results in the following: node_modules/@types/winston/index.d.ts(8,1): message TS4090: Conflicting library definitions for 'node' found at '<dir>/node_modules/@types/winston/node_modules/@types/node/index.d.ts' and '<dir>/node_modules/@types/node/index.d.ts'. Copy the correct file to the 'typings' folder to resolve this conflict.

I get the same error after explicitely npm i @types/node@6 and tsc -p ..

Now comes the ✨, modifying tsconfig.json, excluding node_modules dir, yet reincluding the preferred node typings. Watch out for the precedence of include (weakest), exclude and files (strongest).

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs"
    },
    "exclude": [
        "node_modules"
    ],
    "files": [
        "node_modules/@types/node/index.d.ts"
    ]
}

And tadaam, tsc -p . works.

And now if I npm install a package containing its own definitions then those definitions won't be ignored. But if I don't list node types explicitely in the tsconfig.json (node_modules/@types/node/index.d.ts) then all node-related stuff (e.g. process) will be unavailable.

Honestly, I don't understand it but I can get used to it...

@blakeembrey
Copy link
Contributor

blakeembrey commented Aug 16, 2016

@RyanCavanaugh A simple solution would be to use peerDependencies for environmental typings. Using * isn't always possible, you might be relying on a feature introduced in 6.0 for example. However, it also doesn't make sense that if I'm using 4.0 that there's no errors from TypeScript until I run into a runtime error. If what you're saying is that adding @types/node is true, this makes even less sense because node is an outlier here - what about regular semver modules that can actually have two versions that are incompatible (E.g. sub-dependency on 1.0 and local dependency on 2.0) - TypeScript literally won't be able to ever compile this?

@igabesz
Copy link
Author

igabesz commented Aug 16, 2016

@blakeembrey You can repeat the steps listed above to double-check.

Note that generally using different versions of the same package wouldn't mean any problem, npm 3 starts creating sub-node_modules folders in case of conflicts. The problem emerges only when there is conflict in global stuff (window, global, process, etc.) - which is a rare case, mostly environmental.

PeerDeps would worth a try. But then you have to install additional peerDeps manually for a single @types module? :-/

@blakeembrey
Copy link
Contributor

blakeembrey commented Aug 16, 2016

You would install one @types module, most likely once ever since that's all that is actually global (E.g. @types/node which you had to just do anyway).

I know how NPM works, I was trying to deconstruct how TypeScript resolves here because two dependencies do exactly that - they end up on the same level when they are compatible and if they are incompatible only one will end up on the top-level while one will end up in a sub-module. This is because in the case of three, it's more efficient to de-dupe by putting the version two are compatible at the top level and making the one incompatible version a sub-module.

By the following logic, it means TypeScript resolution will break if it's following the reasoning I've inferred from @RyanCavanaugh's comment above (that installing to the top-level will make that one take precedence).

Edit: If it is purely globals that resolve this way, then yes, my comments are invalid and shouldn't be considered. Haven't had a chance to re-create this use-case myself to test myself of having nested duplicate dependencies at different levels.

@mhegazy
Copy link
Contributor

mhegazy commented Aug 17, 2016

@igabesz what version of tsc and what version of npm are you using? i am unable to get a repro for this issue. locally node@6.0.34 is installed at the top level, so this breaks the conflict.

here is my repro:

c:\test\10250>ls
[.]             [..]            app.ts          tsconfig.json

c:\test\10250>type app.ts
import * as express from 'express';
import * as winston from 'winston';

c:\test\10250>type tsconfig.json
{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs"                                                                                         }
}
c:\test\10250>npm -v
3.10.3

c:\test\10250>npm i @types/express @types/winston
c:\test\10250
+-- @types/express@4.0.30
| +-- @types/express-serve-static-core@4.0.31
| | `-- @types/node@6.0.34
| `-- @types/serve-static@1.7.28
|   `-- @types/mime@0.0.28
`-- @types/winston@0.0.27
  `-- @types/node@4.0.30

c:\test\10250>tsc -v
Version 2.1.0-dev.20160815

c:\test\10250>tsc --listFiles
C:/Users/mhegazy/AppData/Roaming/npm/node_modules/typescript/lib/lib.es6.d.ts
c:/test/10250/node_modules/@types/node/index.d.ts
c:/test/10250/node_modules/@types/express-serve-static-core/index.d.ts                                           C:/test/10250/node_modules/@types/mime/index.d.ts
c:/test/10250/node_modules/@types/serve-static/index.d.ts
C:/test/10250/node_modules/@types/express/index.d.ts
C:/test/10250/node_modules/@types/winston/index.d.ts
c:/test/10250/app.ts

@RyanCavanaugh
Copy link
Member

Here's an attempt to explain what our behavior is.

Let's say you have this structure in node_modules (simplified here, every name prefixed with @types in reality):

/A
  /node_modules
    /Z @ 2.4
/B
 /node_modules
   /Z @ 3.1

If A and B import Z, there's no problem -- we use filename identity to distinguish the two. This is true even if Z has a UMD global declaration (first one in wins).

If both A and B use a type reference directive to reference Z, then we're in what we call a "conflict" state -- two things both claim to need some global things, but disagree about which version of the global state will be present.

The way to get out of the 'conflict' state is to locally add a definition of Z:

/A
  /node_modules
    /Z @ 2.4
/B
 /node_modules
   /Z @ 3.1
/Z @  3.3

Now when A and B say /// <reference types="Z" />, we'll load Z @ 3.3 for both. Again, if they used import, they'd get their "own" copies of Z.

@igabesz
Copy link
Author

igabesz commented Aug 17, 2016

@mhegazy My original setup was this:

> npm -v
3.9.5
> tsc -v
Version 2.0.0

NPM update plus clearing-reinstalling node_modules did not solve this issue (3.10.6).
But typescript@next does solve it! Works fine with this:

> tsc -v
Version 2.1.0-dev.20160817

For me, tsc --listfiles has an extra line: S:/W/test/ts10250/node_modules/@types/mime/index.d.ts, but that shouldn't be a problem. Back to typescript@beta brings back the error. Well, 2.1 will solve it, the question is how difficult is to solve this in 2.0.1 -- and how big is the problem if it won't be solved yet. Your call I guess.. :)

@RyanCavanaugh Thanks for this info. I haven't heard about /// <reference types="Z" /> yet; it seems to be very useful. Looking forward to the documentation. microsoft/TypeScript-Handbook#348

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
External Relates to another program, environment, or user action which we cannot control. @types Relates to working with .d.ts files (declaration/definition files) from DefinitelyTyped
Projects
None yet
Development

No branches or pull requests

5 participants