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.

  1. clone https://github.com/falsandtru/spica
  2. checkout v0.0.247.
  3. run npm i
  4. run npm i del@4.1.1
  5. 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)

Most upvoted comments

@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:

/// <reference types="node" />

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:

And when he add some @types definitions, he has to list the all of them. This is wasteful. You are requiring developers to pay this wasteful cost.

You 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.

Developers are required to make some additional efforts to avoid that obstructive behavior. When a feature requires such efforts, it is regarded as a design failure.

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 on node or weback-env or react-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, resolve del’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 the node_modules folder (although admittedly would handle hoisting via lerna 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 default include path). When your node_modules folder contains dependencies for multiple hosts (eg, bundle target and build time) you should run tsc (or whatever tool you use that invokes tsc) 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 your tsconfig.json for your build, eg:

{
  "compilerOptions": {
    "strict": true,
    "target": "es6",
    "module": "commonjs",
    "types": ["node"]
  }
}

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.