TypeScript: Duplicate type declarations with npm link
Using TypeScript 1.7.3.
Suppose I have the below npm packages. The declaration files are generated by TypeScript compiler, and referred to from the other packages by means of the way described here.
package-a
ts src:
export default class ClassA {
private foo: string;
bar: number;
}
ts declaration:
declare class ClassA {
private foo;
bar: number;
}
export default ClassA;
package-b (depends on package-a):
ts src:
import ClassA from 'package-a';
namespace ClassAFactory {
export function create(): ClassA {
return new ClassA();
}
}
export default ClassAFactory;
ts declaration:
import ClassA from 'package-a';
declare namespace ClassAFactory {
function create(): ClassA;
}
export default ClassAFactory;
package-c (depends on package-a and package-b):
ts src:
import ClassA from 'package-a';
import ClassAFactory from 'package-b';
let classA: ClassA;
classA = ClassAFactory.create(); // error!!
The last line causes an error during compilation:
error TS2322: Type 'ClassA' is not assignable to type 'ClassA'.
Types have separate declarations of a private property 'foo'.
When I remove the line private foo; from the declaration of package-a, TypeScript does not emit any error.
However this workaround is a bit painful.
I understand that exposing private properties to declaration is by design (https://github.com/Microsoft/TypeScript/issues/1532). I think TypeScript should ignore private properties when compiling variable assignment. Or is there any better workaround for this?
About this issue
- Original URL
- State: closed
- Created 8 years ago
- Reactions: 42
- Comments: 147 (33 by maintainers)
Commits related to this issue
- Remove private and protected attributes This is too much of a pain in the ass when linking starspot packages against a real app to test. We should just avoid private/protected properties and methods ... — committed to starspot/starspot by tomdale 8 years ago
- Relationship serialization (#21) * Remove private and protected attributes This is too much of a pain in the ass when linking starspot packages against a real app to test. We should just avoid pri... — committed to starspot/starspot by tomdale 8 years ago
- Fix the typescript part at least of `npm link` This is a known bug in typescript: https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-281852127 This workaround adds minimal estraverse t... — committed to Polymer/polymer-analyzer by rictic 7 years ago
- Fix the typescript part at least of `npm link` (#565) This is a known bug in typescript: https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-281852127 This workaround adds minimal est... — committed to Polymer/polymer-analyzer by rictic 7 years ago
- restructure to allow better consumption via visualstudio. (symlinks) see https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-302886203 — committed to Novaleaf/xlib by jasonswearingen 7 years ago
- structural changes to support rapid dev via visual studio. see https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-302886203 — committed to Novaleaf/phantomjscloud-node by jasonswearingen 7 years ago
- structural changes to support rapid dev via visual studio. see https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-302886203 — committed to Novaleaf/phantomjscloud-node-examples by jasonswearingen 7 years ago
- remove heavy/verbose usage of google-cloud modules, and structural changes to support rapid dev via visual studio. see https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-302886203 — committed to Novaleaf/slib by jasonswearingen 7 years ago
- structural changes to support rapid dev via visual studio. see https://github.com/Microsoft/TypeScript/issues/6496#issuecomment-302886203 — committed to Novaleaf/node-chain-proxy by jasonswearingen 7 years ago
- Lots of cleanup. * Use node 6 instead of node 8 / npm 5 because of https://github.com/lerna/lerna/issues/856 * Use urijs instead of the node 7+ URI package. * Don't ask lerna to hoist all dependencie... — committed to magda-io/magda by kring 7 years ago
- Add preact path to tsconfig Workaround for typescript duplicate identifier errors when using npm link (to get preact-material-components typings branch) More info: https://github.com/Microsoft/typesc... — committed to cvuorinen/preact-material-components-typings-test by cvuorinen 7 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
- Add vscode path to tsconfig Workaround for TypeScript duplicate identifier errors when using npm link/lerna More info: Microsoft/TypeScript#6496 Root cause: Microsoft/vscode-extension-vscode#90 Discu... — committed to forcedotcom/salesforcedx-vscode by guw 6 years ago
I’ve just merged a change which will attempt to detect duplicate packages based on their name and version, and use only one. Please try it out using
typescript@nextwhen that is next published.Fair enough, but someone else might 😉
I’ve reproduced this error.
Since there are two installations of
rxjs, we get:Still running into this issue with typescript@2.8.1. Also tried typescript@next and had the same issue.
The root of the problem seems to be that linked packages still reference the type definitions in their own local
node_modulesrather than using the typings from thenode_modulesinto which they are linked, when possible. That combined with the fact that:node_modulesas well as thenode_modulesin the linked packagenode_modulesto a value returned by the linked package whose type is defined in its ownnode_modulesI’ve been able to work around this issue using the
pathsconfig variable. For modules whose definitions come from@types/*, as suggested here, you can simply use:In the case where you run into this issue with a package that comes bundled with type definitions which define classes or globals, you have to add them manually. For example,
rxjs:tsconfig.jsonexposes a path mapping, add duplicated dependencies intopaths, so it will be loaded from the rightnode_modulesinstead of linked one.I ended up not using
npm link, and this does not matter any more for me.For easier usage, you can also set the paths like this:
Sorry for bugging again with this issue, but it is a serious drag on our project not being able to do
npm linkwhile we are making changes. I would love to help out with a PR if one of the current TypeScript contributors could give me a little guidance on where to start looking in the codebase.I just ran into this issue and it is a major problem for us because we use try to split up our back end into many small libraries. During development, we often need to npm link our repos. The specific issue I ran into which prompted me to find this is the use of rxjs Observables and interfaces:
This works if I don’t
npm link, but when I do, I get:I’m using typescript 2.0.3 and I’m seeing this error with Observable as described above, e.g.
This is a pretty brutal problem that will only affect more complex codebases, and it seems impossible to work around without taking drastic measures, so I hope I can convince you all to give it the attention it deserves. 😄
It’s most noticeable in cases where you have an app that depends on Dependency A, Dependency A depends on Dependency B and vends objects that contain types from Dependency B. The app and Dependency A both
npm linkDependency B and expect to be able to import types from it and have them describe the same thing.This results in deep error messages, and I’m on the verge of going through and eliminating all of the
privateandprotectedproperties in my libraries because I’ve already lost so much time to this:Really appreciate you all looking into this; thank you!
Can confirm this is working for me now with
typescript@2.6.0-dev.20170908. Big thanks to @andy-ms - this is a game changer!I wanted to provide an update. From an offhand exchange I had with @mhegazy, here’s what we’re thinking.
@heruan I will try to set that up.
One, FYI, though. I think I may have found a work around. The problem is resolved if I
npm link rxjsin both ProjectA and ProjectB. This sort of makes sense because in that case, both ProjectA and ProjectB are using the same exact rxjs files. Without that, they are technically using different files (even though the same version):If you just
npm link ProjectAfrom ProjectB, then:But if you
npm link rxjsin both then both of those rxjs references will be symlinked to the same exactly global npm location.Anyways, this is obviously still not ideal, but at least something that can move us forward.
Also…not sure if this is relevant or matters (will see once I set up the test project), but my two libs (i.e. ProjectA and ProjectB) are actually private npm repos.
The issue I’m seeing in my Lerna repo is somewhat involved, so I made a stripped-down version of it at https://github.com/seansfkelley/typescript-lerna-webpack-sadness. It might even be webpack/ts-loader’s fault, so I’ve filed https://github.com/TypeStrong/ts-loader/issues/324 over there as well.
Thanks for all the work in analyzing and documenting this issue. We’re having the same problem in some of our code bases. We ported some projects to properly use
package.jsondependencies but are now seeing this when usingnpm linkduring development.Is there anything I can help to solve this issue?
Also ran into this when using a react project that has
@types/reactinstalled and linking it to an app that also uses@types/react@grofit I think the problem in your situation is that you have two declarations of a module, where what you need is a module and an augmentation. You should be able to do
mongo-promisification.d.tslike this:Without the
import, you are in an ambient context and are writing a new declaration, not an augmentation.(Handbook ref)
This is a huge blocker when working with monorepos. Really hope this will no longer delayed and finds its way into 2.5.
Edit: 😮
Peer dependencies were invented for a situation where a plug-in wants to say what it goes with. They sort of work okay for that case. But they cause a lot of problems if people start converting all their dependencies to peer dependencies, hoping to avoid “npm install” duplication.
We spent months banishing them from our internal git repos. When used as I think you’re suggesting, peer dependencies allow you to solve a side-by-side versioning problem by trading it for several new problems:
the entire tree is inverted, i.e. every package now becomes responsible for taking a hard dependency on packages that used to be indirect dependencies, in many cases without any idea what it is for
if a peer dependency is removed, these hard dependencies will probably never get removed
package authors are tempted to use broad ranges for their peer version patterns, claiming to work with versions that they never actually tested against; broken builds suddenly become the consumer’s problem
Hi guys, Someone found a solution ?? I have the same issue when I link a project with another with RxJS. Thanks 😉
@xogeny I know, I’m trying too, I’d love to see it resolved correctly 😄 I read the linked issues, but it they are all designed to resolve the realpath of a symlink which implies if you have two (real) files they’ll still conflict because they’ll resolve to different locations. Which is what happens when you
npm linkfrom one project into another as both would have their own dependencies that can differ with re-exported symbols from thenpm linked package.Edit: I can confirm, all the issues are because of two files.
npm linkwould trigger it because it’s simple to have a dependency in a repo that you just linked that is the same dependency as in the project you linked to. A simple repro would be to do annpm installof the same dependency at two different levels of an application and watch them error out.FYI, I’ve also ended up here as a result of using
npm linkand getting this error. Has anybody found a workaround for this?there are actually two files on disk with two declarations of ClassA. so the error is correct. but we need to consider node modules when we compare these types. this issue has been reported before in https://github.com/Microsoft/TypeScript/issues/4800, for Enums we changed the rule to a semi-nominal check. possibly do the same for classes.
This still doesn’t work for me when I’m trying to link 2 packages, each having a dependency on
rxjs. I’m usingrxjs@5.5.6andtypescript@2.6.1. Both packages are using the exact same version. Does anyone have a workaround for this?My team has hit this problem pretty frequently when linking our own internal libraries during development.
Recently we found a tool named yalc which mitigates this really well and actually makes for a pretty good dev loop. (As a tl;dr instead of linking the whole package it runs the prepublish scripts and copies the result to a linked folder).
Don’t think this has been mentioned yet, but the super-shorthand way of working around this issue is:
The
pathsentry above basically says: for any module, first look it up in thenode_modulesfolder in the root of the project, then fallback to the normal rule (recursive walk up the directories from where the import has occurred).(Please correct me if I’m wrong, I’m still a relative newcomer to TS).
The entry in
pathsappears to be relative to thebaseUrl. So, if you setbaseUrlto be in a subfolder, you need to change your paths definition accordingly. E.g.:@mikehutter we’ve recently added some guidance in Angular CLI for working with linked libs, and it seems like in your case you’re missing the TypeScript paths configuration for RxJs in your consumer app. See https://github.com/angular/angular-cli/blob/master/docs/documentation/stories/linked-library.md for more info.
@mikehutter In your project which uses your library (not in the library itself), do something like this in your tsconfig:
You may need some slight adjustment to this depending on where your tsconfig and source code result.
(Like others in this thread, I’d love to see some TypeScript improvement which makes this unnecessary.)
@nicksnyder you should be able to do something like this:
That way, B & C are pointing at the exact same files that A is.
Also, note that this is not just a problem with
npm link, you will get this problem too on production builds, when your shared dependencies point to a different version.ie. ProjectA needs
rxjs@4.9.1and ProjectB usesrxjs@4.9.2When you install ProjectA as a dependency in ProjectB you will too have duplicated types, since there will be for example two
Observabledeclarations, one innode_modules/rxjsand one innode_modules/project_a/node_modules/rxjsYou can get around this by allowing rxjs version in ProjectA to be something like
~4.9.0, so thatnpm installdoesn’t need to download its own version, and will instead use ProjectB version. But keep in mind that this is not only a development workflow issue.@mhegazy Well I started getting these errors like the one above (except I was using
Observablefromrxjs, i.e., "Type ‘Observable’ is not assignable to type ‘Observable’). This, of course, seemed odd because the two I was referencingObservablefrom exactly the same version ofrxjsin both modules. But where the types “met”, I got an error. I dug around and eventually found this issue where @kimamula pointed out that if you usenpm link, you’ll get this error. I, like others, worked around this (in my case, I created a duplicate interface of just the functionality I needed in one module, rather than referencesrxjs).Does that answer your question? I ask because I don’t think my case appears any different than the others here so I’m not sure if this helps you.
I’m using Lerna which symlinks packages around and seeing the issue there as well. Typescript version 2.0.3.
Unfortunately Lerna and its symlinks are a hard requirement, so I used this nasty workaround to get this to compile fine while still being properly type-checkable by consumers:
The class is very small so it wasn’t that arduous, and I don’t expect it to change really ever, which is why I consider this an acceptable workaround.
@dakaraphi Thanks! It looks like the error is due to
vscode.d.tsbeing written as a global, ambient declaration rather than as an external module. I’ve created Microsoft/vscode-extension-vscode#90.I’m running the latest test build (Windows 10 64 bit) and this doesn’t seem to be fixed for me.
Reproduction
Structure
Run
a/index.ts
b/index.ts
Run
The types imported from the different versions of the libraries (and the corresponding
@typespackages) can be both equal and different (if the library updates, the type definition should update as well to reflect the changes).It’s not just about
npm linkwhich can be replaced or not used, but about having multiple versions of the same@types/xxxpackage installed at the same time, often pulled in from dependencies which provide TypeScript declarations that depend on standard declarations, as me and other people reported. The number of such packages will grow with more adoption of TypeScript in the Node community.That’s a good insight. Have you looked at pnpm? It is a small step forward from the current node_modules design, but a very effective one if I understand it correctly.
To be honest, I’m not even sure if this should be “fixed” in Typescript, as there also a problem with plain JavaScript: The instanceof operator will return false if the object’s class differs from the provided class.
Thus, I’d rather look forward to a better alternative to npm link that maybe hooks into require(), first.
@leovo2708 module ‘foo’:
client code in other module, that depends on module ‘foo’:
@mhegazy I am on
Version 2.2.0-dev.20161129and still having the issue. The specific issue is that I have one project (let’s call it ProjectA) that contains an “interface” (using a class, but that is so I can use the class as a token for Angular 2 DI) as follows:Then in a completely separate project (let’s call it ProjectB) that has a class which implements the interface from the first project like this:
When I do a normal typescript compile for ProjectB it works fine. But if I
npm link ProjectAfrom the ProjectB root directory and then runtscagain I get:i have a workaround which works great for commandline, but visual studio still gets all messed up: https://github.com/Microsoft/TypeScript/issues/11107#issuecomment-254003380
my new workaround for Windows + Visual Studio 2015 is to robocopy my
xlibLibrarysrcanddistfolder into thenode_modules\xlib\srcandnode_modules\xlib\distfolders of the consuming project.here is the significant part of my robocopy batch file script if anyone wants it: