During a TypeScript migration, tsc reports errors that originate inside .d.ts files under node_modules. What does setting skipLibCheck to true in tsconfig.json actually skip, and what do you give up by leaving it on?
answer
- about declaration files, not your code
- all .d.ts, not just node_modules
- still used, just not verified
- your own shims stop being checked
- fast, and usually permanent
basics
~20 sskipLibCheck stops tsc type-checking the contents of every declaration file — dependencies' and your own alike, including hand-written shims. Your code is still checked against those declarations, so what you give up is any warning that a declaration file is itself wrong or inconsistent.
solid answer
~50 sIt skips type checking of all `.d.ts` files, not just the ones in `node_modules`. That includes your own hand-written shims and any declarations your build emits. What it does not do is stop the compiler *using* those declarations: your `.ts` files are still fully checked against whatever they declare, so it is not a general error mute. It exists because dependency type errors are usually not your problem — two packages disagreeing about a shared type, a package written for a newer compiler, duplicate copies of the same `@types` package — and it also cuts check time noticeably. The cost is that a declaration file which is internally wrong now passes silently, and your own shims stop being validated, so a mistake there types your call sites incorrectly with no diagnostic. Most projects run with it on permanently; if you publish declaration files, verify them with a separate check that does not skip.
go deeper
Know that skipLibCheck is a tsconfig option about declaration files — it stops the compiler checking the contents of .d.ts files, and it is commonly enabled.
Draw the distinction that carries the answer: declarations are still used to check your code, only their own contents go unverified, and the scope is every .d.ts including your own shims.
Explain why the errors arise — conflicting @types versions, a package built for a newer compiler, a lib mismatch — and what you do to keep coverage over the declarations you own, especially when publishing them.
Own the position that this is a permanent setting for applications and a deliberate risk for publishers, and put the compensating control in place: a separate check of emitted declarations before release.
## What the flag does `skipLibCheck` tells the compiler not to type-check the *contents* of declaration files. "Declaration file" here means any `.d.ts`: the ones shipped inside packages, the `@types` packages you install, the built-in library files that describe the language and DOM, your own hand-written shims, and the declarations your own build emits. The crucial distinction is between *checking a declaration* and *using a declaration*. With the flag on, tsc still reads every declaration file and still uses the types in it to check your source. If a package declares `parse(input: string): number`, your call sites are checked against exactly that signature. What is skipped is the compiler verifying that the declaration file is internally coherent — that the types it references exist, that its own members are consistent, that it does not contradict itself. So this is not a blanket "ignore type errors" switch, and describing it that way in an interview is a red flag. ## Why the errors appear in the first place During a migration you inherit a dependency tree that was never checked, and several situations produce errors you cannot fix: - **Two versions of the same types.** Different packages depend on different versions of a `@types` package, so two declarations of the same global or interface end up in the program and conflict. - **A package written for a newer compiler.** Its declarations use syntax or built-in types your version does not know. - **A mismatch with `lib`.** A dependency's declarations reference DOM types while your `lib` setting includes only the Node-ish surface, or vice versa. - **A declaration file that is simply wrong.** Plenty of published types have never been checked against the code they describe. None of these are fixed by editing your own code, which is why blocking on them is pure friction. There is a secondary benefit too: skipping this work makes the type check meaningfully faster on a large dependency tree. ## What it costs you The cost lands on the declaration files you own. A migration typically produces shims for untyped dependencies, and with the flag on those shims are no longer validated. If a shim declares the wrong return type, tsc reports nothing — it trusts the declaration and checks your call sites against the lie: ```ts // legacy-lib.d.ts — a hand-written shim that is simply wrong declare module "legacy-lib" { export function version(): number; // it really returns a string } ``` Every call site now believes it holds a `number`. `version().toFixed(2)` compiles, and fails at runtime. The compiler never had a way to catch this — it cannot compare a declaration against a JavaScript implementation — but with `skipLibCheck` on you also lose the checks it *could* have done on the file's internal consistency. The same applies to declarations you publish. If your package emits `.d.ts` files and you never check them, you can ship types that do not compile for your consumers even though your own build is green. ## How to use it well Turn it on. In practice almost every real project runs with `skipLibCheck: true`, and it is on by default in many project templates — the friction of policing other people's declaration files is not worth the value. Treat the migration framing honestly: it is a stopgap in the sense that it unblocks you immediately, but for most applications it is a permanent, reasonable setting. What should not become permanent is the loss of coverage over declarations you own: - If you **publish** a package, run one extra check without the flag, or check the emitted `.d.ts` files separately, so you do not ship broken types. - Keep hand-written shims minimal and prefer replacing them with real `@types` packages or upstream types as they become available; a shim is an assertion nobody verifies. - Revisit occasionally. If it was turned on to work around one bad dependency, that dependency may have been fixed, and a clean run without the flag is a cheap thing to try. ## What it will not do for you It will not silence errors in your `.ts` files, it will not make an untyped package typed, and it will not resolve a genuine version conflict — it only stops that conflict being reported from inside the declarations. If your code breaks because two libraries disagree about a shared type, you will still see it at the point where your own code sits between them.
- If skipLibCheck is on, will an error in your own .ts file caused by a dependency's wrong types still appear?Yes. The declarations are still used to check your code, so if a library declares a function as returning a number and you use the result as a string, that error is reported in your file. The flag only suppresses diagnostics raised inside the `.d.ts` files themselves — it is not a way to mute errors caused by bad dependency types.
- You publish a library with generated .d.ts output. What should you do differently?Verify the emitted declarations with a check that does not skip them — a separate compile of the built output, or a dedicated CI step with the flag off. Otherwise your own build stays green while consumers, who compile your declarations into their programs, hit errors you never saw. Publishing types is a contract, and this is the only place it is enforced.
- Is skipLibCheck a workaround for a package that ships no types at all?No — those are different problems. A package with no declarations produces a module-resolution error at your import, not an error inside a `.d.ts`. That is solved by installing a `@types` package or writing a declaration for the module; skipping declaration checks does nothing for it.
saying these in an interview costs you the question
- Thinks it suppresses type errors in your own code
- Believes it only affects files in node_modules
- Assumes it makes untyped packages typed
- Leaves it on while publishing generated declarations
- Calls it unsafe and refuses to use it at all