Ts-node: Cannot find module defs using typeRoots

Created on 14 Sep 2018  路  13Comments  路  Source: TypeStrong/ts-node

If you have a config like this:

{
  "compilerOptions": {
    ...
    "typeRoots" : ["./typings"]
  }

And a directory structure like this:

-- tsconfig.json
  -- typings
    -- index.d.ts

ts-node will not be able to find the module defs in index.d.ts

ts-node will work if you have something like:

-- tsconfig.json
  -- typings
    -- <library_name>
      -- index.d.ts

Typescript will work with either setup.

Most helpful comment

@blakeembrey Can you be more specific? I have read the README several times over and the only workaround I have been able to find is the one @shusson mentioned, which is to break my module declarations out into index.d.ts files in folders named after the module and add a typeRoots section.

My tsconfig.json does not have a reference to files, include or exclude, in fact it is quite small/simple:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2015",
        "noImplicitAny": true,
        "allowJs": false,
        "noEmit": true,
        "strict": true,
        "lib": [ "es2018", ],
    }
}

The readme says:

TypeScript Node does not use files, include or exclude, by default.

Great, I am not using any of those compiler options so that doesn't apply to me.

It then goes on to say:

For global definitions, you can use typeRoots:

Great, I have some global module definitions so I can put them into typeRoots.

Unfortunately, this is where things fall apart, putting my module definitions into a file in typeRoots does not work as I expect.

I somewhat suspect (after reading the readme 3 more times while drafting this response) that the magic is in this statement:

A types package is a folder with a file called index.d.ts or a folder with a package.json that has a types field. -- TypeScript Handbook

If I'm interpreting that correctly, when combined with your insistence that "the answer is in the README", it is saying that the typeRoots mentioned previously only works if the types are setup as type package folders within the typeRoots directory?


Since I don't want to be yet another person who complains but offers no solutions, I would recommend rewording that section to be more clear. Looking through GitHub issues, there are about a dozen issues with people struggling with this same problem, which suggests to me that the way it is currently worded is not immediately apparent to the casual ts-node user.

Perhaps something like:

For global definitions, you can use the typeRoots compiler option. This requires that your type definitions be structured as type packages (not loose TypeScript definition files). More details on how this works can be found in the TypeScript Handbook.
Example tsconfig.json:

{
  "compilerOptions": {
    "typeRoots" : ["./node_modules/@types", "./typings"]
  }
}

Example project structure:

<projcet_root>/
-- tsconfig.json
-- typings/
  -- <module_name>/
    -- index.d.ts

Example module declaration file:

declare module '<module_name>' {
    // module definitions go here
}

It鈥檚 working accidentally with tsc because your configuration includes it, not because TypeScript looks it up.

What part of my configuration is causing this to happen? I have included my tsconfig.json, and in fact tsc works without the typeRoots section at all. Is there some other config you are referring to besides tsconfig.json? I'm just running tsc in the directory with the tsconfig.json file and it is successfully compiling, I am not passing in any additional compiler options or providing any files.

All 13 comments

See the README. This is not the correct structure. It most likely works with TypeScript because you have it explicitly or explicitly in your includes.

Ok @blakeembrey , but then ts-node should have worked with with the --files flag right?

Yes, probably. In any case this is based on the TypeScript compiler API, so I鈥檇 need a full reproduction if you believe it鈥檚 a bug.

Sorry my mistake. I couldn't reproduce with a simple setup. Looks like it's to do with our own structure.

I'm running into this problem after upgrading to ts-node 7.x and typescript 3.x. Everything was working with ts-node 6.x and typescript 2.7.x.

Did you ever figure out your issue?

Note: my tsconfig.json doesn't have any include/excludes and npx tsc works correctly. The problem is specific to when running npx ts-node myscript.ts.

Note2: This is a common ask in the issues here, but unlike most of them, me and the OP are following the instructions in the README and adding "typeRoots": [ "./typings" ] where our custom module definitions are defined. However, this doesn't appear to actually work.

See the README. This is not the correct structure.

Can you provide some details on why this is not the correct structure? It is accepted by TypeScript compiler without problem. There are two supported (as far as I know) ways to have modules. One is by creating some .d.ts file in typeRoots directory (e.g., my.d.ts) with one or more declare module 'whatever' { ... } blocks in it, and another is to create a a folder named myModule with a file named index.d.ts in it that contains declare module 'myModule' { ... }. Both methods appear to be fully supported by the compiler, but only the latter method appears to be supported by ts-node.

At the least, the README should be updated to mention that you cannot use the former style, and ideally a brief blurb or link to the reasoning would be appreciated.

@MicahZoltu This is in the README. Both styles are supported. Please read the section about types to understand why it鈥檚 not working for you.

FWIW, the first method you describe is not supported by TypeScript. It鈥檚 working accidentally with tsc because your configuration includes it, not because TypeScript looks it up. This is in the README. Feel free to share your project/structure if you feel this is incorrect.

@blakeembrey Can you be more specific? I have read the README several times over and the only workaround I have been able to find is the one @shusson mentioned, which is to break my module declarations out into index.d.ts files in folders named after the module and add a typeRoots section.

My tsconfig.json does not have a reference to files, include or exclude, in fact it is quite small/simple:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2015",
        "noImplicitAny": true,
        "allowJs": false,
        "noEmit": true,
        "strict": true,
        "lib": [ "es2018", ],
    }
}

The readme says:

TypeScript Node does not use files, include or exclude, by default.

Great, I am not using any of those compiler options so that doesn't apply to me.

It then goes on to say:

For global definitions, you can use typeRoots:

Great, I have some global module definitions so I can put them into typeRoots.

Unfortunately, this is where things fall apart, putting my module definitions into a file in typeRoots does not work as I expect.

I somewhat suspect (after reading the readme 3 more times while drafting this response) that the magic is in this statement:

A types package is a folder with a file called index.d.ts or a folder with a package.json that has a types field. -- TypeScript Handbook

If I'm interpreting that correctly, when combined with your insistence that "the answer is in the README", it is saying that the typeRoots mentioned previously only works if the types are setup as type package folders within the typeRoots directory?


Since I don't want to be yet another person who complains but offers no solutions, I would recommend rewording that section to be more clear. Looking through GitHub issues, there are about a dozen issues with people struggling with this same problem, which suggests to me that the way it is currently worded is not immediately apparent to the casual ts-node user.

Perhaps something like:

For global definitions, you can use the typeRoots compiler option. This requires that your type definitions be structured as type packages (not loose TypeScript definition files). More details on how this works can be found in the TypeScript Handbook.
Example tsconfig.json:

{
  "compilerOptions": {
    "typeRoots" : ["./node_modules/@types", "./typings"]
  }
}

Example project structure:

<projcet_root>/
-- tsconfig.json
-- typings/
  -- <module_name>/
    -- index.d.ts

Example module declaration file:

declare module '<module_name>' {
    // module definitions go here
}

It鈥檚 working accidentally with tsc because your configuration includes it, not because TypeScript looks it up.

What part of my configuration is causing this to happen? I have included my tsconfig.json, and in fact tsc works without the typeRoots section at all. Is there some other config you are referring to besides tsconfig.json? I'm just running tsc in the directory with the tsconfig.json file and it is successfully compiling, I am not passing in any additional compiler options or providing any files.

Feel free to submit a PR for the documentation change. I鈥檓 on mobile so unable to link to the section correctly, but that鈥檚 the correct section. It works with TypeScript by default because, when you don鈥檛 specify the files/includes, it would be traversing your entire directory for TypeScript files. I鈥檓 not sure if the expectation here otherwise, since tsc appears to be compiling everything right?

Yes, tsc is compiling everything when run from the directory containing the tsconfig.json. IIUC, you are saying that this means that tsc will naturally find any types you have in any directory that is being compiled, and normally you would only need to include typeRoots if you want to pull type data from a directory that is _outside_ the directory being compiled. However, since ts-node doesn't compile anything by default other than the referenced file (and things that file references) it won't find any type files automatically unless they are located on the import lookup path (such as in node_modules).

Pretty much, yes. To TypeScript, the global declarations can be understood any time (the structure is usually more import with the module style declarations). But yeah, since we aren鈥檛 globing the entire directory it needs to follow the structure TypeScript supports. Fortunately this is TypeScript native suppport, so fixing for ts-node does not change behaviour on the compiler.

I鈥檇 love to see the documentation PR, it鈥檚 definitely an improvement over what I originally wrote!

PR to edit documentation to hopefully reduce the number of people that show up here in the issues with the same problem: https://github.com/TypeStrong/ts-node/pull/698

Was this page helpful?
0 / 5 - 0 ratings