TypeScript: Unused @types files installed by node modules pollutes and breaks the main project
Even when we don’t use it in the main project. For instance, I’m using del package only in the build script file and it is .js file not .ts file.
https://github.com/falsandtru/pjax-api/blob/master/gulpfile.js
But it polluted and broke the main project:
https://travis-ci.org/falsandtru/pjax-api/jobs/525654172#L512
This is caused by https://github.com/sindresorhus/del/commit/0361dccad6cd8b0b0f02589a26b021f9438f4b91.
You shouldn’t automatically include unused/unimported @types files installed by node modules in the compile targets because they usually won’t be used when they are not imported explicitly (regardless of direct or indirect). Or you should provide a way to exclude unexpected @types files by tsconfig.json to avoid this problem as a temporary workaround.
TypeScript Version: 3.4.0-dev.20190425
Search Terms:
Code
You can reproduce this problem also with https://github.com/falsandtru/spica.
- clone https://github.com/falsandtru/spica
- checkout v0.0.247.
- run
npm i - run
npm i del@4.1.1 - run
gulp ts:dist
Expected behavior:
pass
Actual behavior:
TypeScript error: src/throttle.ts(7,5): Error TS2322: Type 'Timeout' is not assignable to type 'number'.
TypeScript error: src/throttle.ts(28,5): Error TS2322: Type 'Timeout' is not assignable to type 'number'.
Playground Link:
Related Issues:
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Reactions: 11
- Comments: 33 (28 by maintainers)
@DanielRosenwasser I found out the issue, and I don’t know if it’s typescript’s issue or my dependencies, it feels like typescript’s though 🤷
So I found with that very extensive
--explainFilesflag, that I had various transient dependencies that did this in their declaration types:When I removed one, and ran
--explainFilesagain, it would find another. So I removed that. I kept doing that until--explainFilesdidn’t find it anymore. At which point, my original issue of@types/nodesbeing able to be used anywhere in my project is resolved.my particular package “offenders” are:
@react-pdf/renderer: which is here https://github.com/diegomura/react-pdf/blob/master/index.d.ts#L1ts-invariant: which looks like they removed in their source here https://github.com/apollographql/invariant-packages/blob/main/packages/ts-invariant/src/invariant.ts#L1, but the dependency I’m depending on which is@apollo/clientstill uses an old versionsubscriptions-transport-ws: which also looks like they removed in their source here https://github.com/apollographql/subscriptions-transport-ws/blob/danielr/fix-start-dos/src/server.ts#L1, but the dependency I’m depending on which is@apollo/clientstill uses an old versionYou only need to list packages with globals that aren’t explicitly imported, not every package. Things you explicitly import need not be explicitly included via
types.The alternative is worse. (And we could only even consider it recently) - imagine if the behavior of
types: []was the default - now if your package depends onnodeorweback-envorreact-native, you explicitly add a/// <types name="node"/>, since it’s the only way to pull these globals in. Now the choice of which global environment your package requires is locked for all your consumers and can’t be changed, and will potentially conflict with any environment they attempt to provide. This already happens when type authors don’t know better (since having an open ended environment usually requires forward declarations of things like buffers), but changing the default makes it the defacto norm, which is bad.Working out the dependency tree from a package.json wouldn’t magically be any better, either - we’d still see that you depend on
del, resolvedel’s dependency on@types/nodeand load it, and still see your local dependency on@types/react-nativeand load that, too - it’s just a roundabout way of discovering the same thing as the contents of thenode_modulesfolder (although admittedly would handle hoisting vialernabetter, though that’s not too common).There is no change we can make here that qualifies as a “solution” where nobody has to write any config down, and we already have a solution that requires a minimal amount of config. We only ask that you provide the name(s) of the ambient definition packages which are not directly depended upon by references in your code, so that we may load only those specified and those your code actively references (via
importor triple slash reference).Mmmmmm, it is an external problem. I’m going to reiterate for people who find this thread:
There is no bug in TS’s behavior. When not overridden, TS will load all type definitions in your
node_modulesfolder (like a defaultincludepath). When yournode_modulesfolder contains dependencies for multiple hosts (eg, bundle target and build time) you should runtsc(or whatever tool you use that invokestsc) with a--typesoption equal to the ambient declarations that host expects. That means for test, maybe--types mochaor--types jest, for a node server, maybe--types node, for a frontend, maybe--types jquery. In many cases a--typeswith no argument (an empty list) will even suffice, if you don’t rely on any implicit globals. You can include this in yourtsconfig.jsonfor your build, eg:TL;DR: Pass
--typesto the compiler if you have an issue relating to conflicting globals caused by your multiple kinds of dependencies installed into the same place.