microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.95k stars 12.47k forks source link

When adding "/node_modules/@types" to typeRoots, "@types/es6-promise" is found, but "@types/node" is not found unless explicitly added via specifying: "types" : ["node"]. #17241

Closed shirakaba closed 7 years ago

shirakaba commented 7 years ago

TypeScript Version: 2.4.1; with ts-loader version 2.2.2.

Code

Starting with a simple TS file that uses require():

require('es6-promise').polyfill();
// Error TS2304: cannot find name ' require'.

Try to install typings for it via the @types/node package: npm install --save-dev @types/node.

(For reference, here is my tsconfig.json):

{
  "compilerOptions": {
    "watch": true,
    "sourceMap": true,
    "target": "es5",
    "baseUrl": ".",
    "outDir": "./build",
    "allowJs": true,

    "typeRoots": [
        "/node_modules/@types",
        "/types"
    ],

    "module": "commonjs",
    "declaration": false,
    "noImplicitAny": false,
    "noLib": false
  },

  "exclude": [
    "my-library",
    "node_modules",
    "dist"
  ]
}

Expected behavior:

Error should resolve, as all types should automatically be pulled in from node_modules/@types due to it having been specified as a path in typeRoots. Relevant handbook statement here.

Actual behavior:

Error remains, unless "types": ["node"] is explicitly added to the tsconfig.json, or typeRoots is commented out altogether. It seems that the default behaviour of typeRoots is not equivalent to "typeRoots": ["/node_modules/@types"].

ikatyang commented 7 years ago
"typeRoots": [
-  "/node_modules/@types",
+  "./node_modules/@types",
-  "/types"
+  "./types"
],
shirakaba commented 7 years ago

@ikatyang If I use a relative path as above, it now throws several errors due to the types clashing with duplicate identifiers in my nested project, my-library, which has its own node_modules/@types and types folders containing similar dependencies:

../../../.npm-packages/lib/node_modules/typescript/lib/lib.d.ts(1288,11): error TS2300: Duplicate identifier 'Promise'.
my-library/node_modules/@types/es6-promise/index.d.ts(11,15): error TS2300: Duplicate identifier 'Promise'.
my-library/node_modules/@types/es6-promise/index.d.ts(42,19): error TS2300: Duplicate identifier 'Promise'.
my-library/node_modules/@types/es6-promise/index.d.ts(83,2): error TS2300: Duplicate identifier 'export='.
node_modules/@types/es6-promise/index.d.ts(11,15): error TS2300: Duplicate identifier 'Promise'.
node_modules/@types/es6-promise/index.d.ts(42,19): error TS2300: Duplicate identifier 'Promise'.
node_modules/@types/es6-promise/index.d.ts(83,2): error TS2300: Duplicate identifier 'export='.

Is there any way to prevent tsc looking for types inside the my-library folder? I've already added it to exclude (as shown in the tsconfig.json in the original post).

shirakaba commented 7 years ago

I should also note that the project structure is visible at this repo. Note that the tsconfig.json in this repo is subject to change based on this discussion.

ikatyang commented 7 years ago
+"skipLibCheck": true

EDIT: this option won't prevent looking for types inside the my-library, but it will skip the checking for .d.ts files in @types.

shirakaba commented 7 years ago

@ikatyang Do you mean that this option skips the checking for .d.ts files in node_modules/@types (the parent's @types), or my-library/node_modules/@types (my library's @types), or both?

ikatyang commented 7 years ago

both.

skipLibCheck - Skip type checking of all declaration files (*.d.ts). (from TS handbook)

shirakaba commented 7 years ago

I don't understand that sentence (and not understanding the handbook page); how can a declaration file itself have a type?

(By the way, your solution worked, but I must confirm that there will be no side-effects as a result).

ikatyang commented 7 years ago

Type checking for declaration files are similar to ts files, but only allow to use non-actual value, they're all types.

(something.d.ts)

declare const a: any;
declare const a: any; // [ts] Cannot redeclare block-scoped variable 'a'.

About side-effects, if your declaration files have something wrong, you'll get something inferred as any implicitly, and it might break your runtime behavior since any will pass all type-checks, it's not recommended to enable that option..

I'd recommend you to use structure like monorepo to prevent type pollution due to its hierarchy.

(original)

node_modules/
  @types/
my-library/
  node_modules/
    @types/

(monorepo)

packages/
  my-library1
    node_modules/
      @types/
  my-library2
    node_modules/
      @types/
shirakaba commented 7 years ago

I had been using a structure like monorepo before, so I'll revert to that as it seems there are lots of issues with using a nested structure (eg. it is very hard to control where tsc goes looking for things, and as it seems here, there is no way to stop it looking for types throughout the whole package. If nothing else, unnecessary searching slows down build times).

Thank you for your valuable help again on another work day! 🙏