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
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));jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"maxNodeModuleJsDepth": 0
},
"include": [
"*.js"
]
} import { slugify } from "slugify-lite";
console.log(slugify(42));error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.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
1reads the file your import resolves to, and2also 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 ajsconfig.jsonandcheckJsboth 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
- TSConfig Reference: maxNodeModuleJsDepth · TypeScript
- jsconfig.json · Visual Studio Code docs
Last reviewed 20 October 2026.