jsconfig.json option

maxNodeModuleJsDepth

  • Default 0
  • 2 in jsconfig.json
  • Tested with TypeScript 5.9

When a package in node_modules ships only JavaScript, with no .d.ts files and no @types package, TypeScript can read its .js files to work out its types, JSDoc included. maxNodeModuleJsDepth sets how far it goes: 0 reads none of them, 2 reads the file your import resolves to and the JavaScript files it imports in turn, two levels deep.

A file named jsconfig.json defaults it to 2; a tsconfig.json defaults it to 0. At 0, importing such a package gives you any, and under noImplicitAny the import itself is an error (TS7016).

At a glance

Write Keep the jsconfig.json default, 2. Write 0 if reading JavaScript packages makes checking slow
On the command line --maxNodeModuleJsDepth
Type number
Default in TypeScript 5.9 0
Default in a jsconfig.json 2
TypeScript's description Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'.

The description is quoted from TypeScript's own option list (Apache-2.0); see the notices.

Examples

main.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: main.js(2,25): error TS7016: Could not find a declaration file for module 'slugify-lite'. '/project/node_modules/slugify-lite/index.js' implicitly has an 'any' type. Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';` 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. main.js(2,25): error TS7016: Could not find a declaration file for module 'slugify-lite'. '/project/node_modules/slugify-lite/index.js' implicitly has an 'any' type. Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';` 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. main.js(2,25): error TS7016: Could not find a declaration file for module 'slugify-lite'. '/project/node_modules/slugify-lite/index.js' implicitly has an 'any' type. Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';` checked 21 October 2026
import { slugify } from "slugify-lite";error TS7016: Could not find a declaration file for module 'slugify-lite'. '/project/node_modules/slugify-lite/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/slugify-lite` if it exists or add a new declaration (.d.ts) file containing `declare module 'slugify-lite';`

console.log(slugify(42));
maxNodeModuleJsDepth 0: a JavaScript-only package is untyped.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": [],
    "maxNodeModuleJsDepth": 0
  },
  "include": [
    "*.js"
  ]
}
main.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: main.js(4,21): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. node_modules/slugify-lite/index.js(12,16): error TS2551: Property 'lenght' does not exist on type 'string[]'. Did you mean 'length'? 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. main.js(4,21): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. node_modules/slugify-lite/index.js(12,16): error TS2551: Property 'lenght' does not exist on type 'string[]'. Did you mean 'length'? 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. main.js(4,21): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. node_modules/slugify-lite/index.js(12,16): error TS2551: Property 'lenght' does not exist on type 'string[]'. Did you mean 'length'? checked 21 October 2026
import { slugify } from "slugify-lite";

console.log(slugify(42));error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
The jsconfig.json default, 2: the same package read for types.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

What the two examples show

Both examples import the same small JavaScript-only package, slugify-lite, whose function has a JSDoc @param {string}. At depth 0, TypeScript does not look inside the package: the import is TS7016 and slugify is any, so the wrong argument passes unnoticed. At the jsconfig.json default of 2, TypeScript reads the package, picks up the JSDoc, and reports slugify(42).

The package also contains a mistake of its own, words.lenght. Neither example reports it: files read from node_modules this way lend their types to your code, but type errors inside them are not reported.

One thing from inside a package can still show up. If the package imports another JavaScript package that lies past the depth limit, that import is reported as TS7016 in the package's own file, with a path into node_modules. Raising the depth by one, or installing types for the inner package, makes it go away.

Packages that ship types are not affected

When a package has a types entry, a .d.ts file next to its JavaScript, or an @types package, TypeScript uses that and this option changes nothing. It only matters for packages that are JavaScript all the way down.

When to set it to 0

Reading large JavaScript packages takes time, and inferred types from code that was never written for a checker can be loose or odd. If checking is slow or a dependency's inferred types get in the way, set 0 and give the package types another way: install its @types package, or write a one-line declaration such as declare module "slugify-lite"; in a .d.ts file, which types it as any on purpose.

Gotchas

  • The depth counts import steps, not folders or packages: depth 1 reads the file your import resolves to, and 2 also reads the JavaScript files that file imports, in the same package or another one.
  • The option only applies when JavaScript files are allowed in the program (allowJs, which a jsconfig.json and checkJs both give you).

Questions

Why does the same import work in my editor but fail with tsc?

Check the config file name. A jsconfig.json reads JavaScript packages two levels deep by default, a tsconfig.json does not, so the same import can be typed in one project and TS7016 in the other.

Sources

Last reviewed 20 October 2026.