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
--explainFiles
flag, that I had various transient dependencies that did this in their declaration types:When I removed one, and ran
--explainFiles
again, it would find another. So I removed that. I kept doing that until--explainFiles
didn’t find it anymore. At which point, my original issue of@types/nodes
being 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/client
still 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/client
still 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 onnode
orweback-env
orreact-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/node
and load it, and still see your local dependency on@types/react-native
and load that, too - it’s just a roundabout way of discovering the same thing as the contents of thenode_modules
folder (although admittedly would handle hoisting vialerna
better, 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
import
or 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_modules
folder (like a defaultinclude
path). When yournode_modules
folder contains dependencies for multiple hosts (eg, bundle target and build time) you should runtsc
(or whatever tool you use that invokestsc
) with a--types
option equal to the ambient declarations that host expects. That means for test, maybe--types mocha
or--types jest
, for a node server, maybe--types node
, for a frontend, maybe--types jquery
. In many cases a--types
with no argument (an empty list) will even suffice, if you don’t rely on any implicit globals. You can include this in yourtsconfig.json
for your build, eg:TL;DR: Pass
--types
to the compiler if you have an issue relating to conflicting globals caused by your multiple kinds of dependencies installed into the same place.