Force tsc to Ignore node_modules: Fix TS Errors
Run tsc --noEmit on a project that compiles fine in the editor and the terminal lights up with type errors from inside node_modules. The fix is skipLibCheck, plus two
The error that won't go away#
Run tsc --noEmit on a project that compiles fine in the editor, and the terminal lights up with a dozen type errors, all from inside node_modules. That is the scenario in the original Stack Overflow question (340 upvotes, 299,000 views), and the output looks like this:
node_modules/some-library/dist/index.d.ts:45:18 - error TS2314: Generic type 'Array<T>' requires 1 type argument(s).
node_modules/another-package/types.d.ts:12:5 - error TS2430: Interface 'Options' incorrectly extends interface 'Config'.The project builds and runs without issues. The project's own source code has zero errors. But tsc insists on crawling through every .d.ts file in node_modules and reporting problems that aren't yours to fix. Adding "exclude": ["node_modules"] to tsconfig.json, the first thing the Stack Overflow asker tried, does nothing.
The fix is a single compiler option most projects should already have enabled: "skipLibCheck": true. If that alone doesn't silence every false positive, there are two more precise settings that close the remaining gaps.
Why exclude doesn't stop tsc from checking dependencies#
The exclude array in tsconfig.json controls which files TypeScript considers part of your program. It prevents tsc from discovering .ts and .tsx files in those directories as standalone compilation units. But it does not stop type resolution.
When your code contains import { something } from "a-library", TypeScript follows the module resolution algorithm to find the package's entry point — typically the types or typings field in its package.json, or an index.d.ts file. Once resolved, that declaration file becomes part of the compilation graph. exclude cannot sever that link because the file is now a dependency of your source code, not an independently discovered file.
The relevant code path in a typical tsconfig.json looks like this:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}That exclude array prevents tsc from walking node_modules as a root directory. But the moment any file in src imports from a package, tsc resolves the package's declaration files and type-checks them. The errors you see are real type violations inside those .d.ts files — they just aren't your responsibility to fix.
The fix: skipLibCheck and two backup options#
The primary fix is adding "skipLibCheck": true to compilerOptions. This tells TypeScript to skip type-checking all declaration files (.d.ts), regardless of where they live. It doesn't affect type-checking of your own .ts and .tsx files.
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["node_modules", "dist"]
}That single line eliminates every error originating from a .d.ts file. Your own source code still gets full strict checking. The trade-off is the one TypeScript's own docs name: two libraries can each ship a declaration for the same global or ambient type in a slightly different, inconsistent way, and skipLibCheck is what lets that inconsistency through instead of failing the build — which is also why skipLibCheck ships enabled by default in Next.js and Vite's templates, and why Create React App's react-scripts auto-writes it into tsconfig.json as a "suggested" value whenever it's missing (verifyTypeScriptSetup.js).
Step by step#
- Open
tsconfig.jsonat the project root. - Locate the
compilerOptionsblock. - Add
"skipLibCheck": trueinside it. - Save and run
tsc --noEmitagain.
Verify the fix#
Run the same command that produced the errors:
npx tsc --noEmitExpected output:
# No output — clean exit code 0If you were seeing errors from a specific package and they're gone, the fix worked. To confirm skipLibCheck is active without relying on the absence of errors, you can run tsc with the --listFiles flag and grep for .d.ts files from node_modules:
npx tsc --noEmit --listFiles | grep "node_modules.*\.d\.ts" | head -5# Still lists the files — they're part of the program, just not type-checked
node_modules/typescript/lib/lib.es2020.d.ts
node_modules/@types/react/index.d.tsThe files still appear because they're still resolved and used for type information. They just aren't checked for internal consistency. That's the behavior you want.
Two variants that still trip people up#
Variant A: Errors from @types/* packages. skipLibCheck covers these because they ship .d.ts files. If you're still seeing errors from @types/react or similar, confirm skipLibCheck is spelled correctly and inside compilerOptions — placed at the top level of tsconfig.json instead, it has no effect.
Variant B: Errors from a package that uses export = syntax. Some older packages declare types with export = instead of ES module syntax. If esModuleInterop is off, tsc may report import-style errors even with skipLibCheck enabled because the error is about how your code imports the package, not about the declaration file itself. The fix for this specific case is enabling esModuleInterop:
{
"compilerOptions": {
"skipLibCheck": true,
"esModuleInterop": true
}
}This is a separate class of error — it's a module-resolution mismatch, not a type-checking error inside the dependency. The import side of that problem is covered in Fix TS2305: Module Has No Exported Member in TypeScript.
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
Why this keeps happening across projects#
The root cause is that TypeScript's default behavior prioritizes soundness over pragmatism. When strict mode is on, tsc applies the same rigorous checks to every file in the compilation graph — including third-party declarations you didn't write and can't fix. Library authors ship .d.ts files with varying levels of strictness compliance, and a single generic constraint violation in one package can cascade into dozens of errors in your build.
The invariant to remember: exclude controls file discovery, skipLibCheck controls file checking. They solve different problems. If you're setting up a new project, enable skipLibCheck from day one — it is the default across every major TypeScript starter template for exactly this reason.
For projects where you want an extra safety net, you can combine skipLibCheck with a CI step that runs tsc without it on a schedule — not on every commit, but weekly — to catch regressions in your own type declarations. That way you get fast builds and still have visibility into dependency type health.
If a dependency ships no types at all instead of a flawed .d.ts, the diagnosis is different: see Fix TS7016: Could Not Find Declaration File for Module. And if you've set up path aliases that tsc isn't resolving, the fix is in Fix tsconfig Paths Not Working in Next.js.
FAQ#
Does skipLibCheck affect type safety in my own code? No. It only skips checking .d.ts files. Your .ts and .tsx files still get full type-checking with all the strictness settings you've configured. The types from dependencies are still used for inference and error detection in your code — they just aren't validated for internal consistency.
Why does tsc check node_modules even when I use "exclude"? Because exclude only prevents TypeScript from discovering files as top-level compilation units. When your code imports a package, TypeScript resolves that package's type declarations and includes them in the compilation graph. The only way to stop type-checking those resolved declaration files is skipLibCheck.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix: compilerOptions.paths Must Not Be Set (Alias Imports)
A tsconfig "paths" alias that works with the TypeScript compiler but breaks Create React App's build. Why CRA specifically forbids it, and three ways to keep clean "@/..." imports anyway.
Fix: Directory import is not supported resolving ES modules
Why Node throws ERR_UNSUPPORTED_DIR_IMPORT under "type": "module", three fixes reproduced on Node 24, and why the old specifier flag no longer helps.
Fix return old(m, filename) ERR_REQUIRE_ESM in ts-node
Why the ts-node frame appears, why the dynamic import() the error suggests still fails under module commonjs, and five fixes tested on Node 18 and 24.


