Question
TypeScript: Ignore node_modules Errors with skipLibCheck and lib
Question
I am running TypeScript in watch mode with this command:
.\node_modules\.bin\tsc.cmd --watch -p .\tsconfig.json
TypeScript reports errors from node_modules/@types/node/index.d.ts:
error TS2304: Cannot find name 'Map'.
error TS2304: Cannot find name 'Set'.
error TS2304: Cannot find name 'Symbol'.
error TS2304: Cannot find name 'WeakMap'.
error TS2304: Cannot find name 'WeakSet'.
I added node_modules to the exclude list in my tsconfig.json, but the errors still appear:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"sourceMap": true,
"strict": false,
"noImplicitAny": false,
"strictPropertyInitialization": false,
"esModuleInterop": true
},
"include": ["src/*"],
"exclude": [
"node_modules",
"./node_modules",
"./node_modules/*",
"./node_modules/@types/node/index.d.ts"
]
}
What am I doing wrong, and how can I stop or fix these errors from Node type declarations? I am using VS Code and TypeScript 2.9.2.
Short Answer
exclude controls which source files TypeScript discovers as project roots; it does not prevent TypeScript from loading declaration files required by an import or by installed @types packages. The Map, Set, and Symbol errors usually mean the configured JavaScript library is too old. Add an ES2015 library, use skipLibCheck when appropriate, or restrict automatically included type packages with types.
Concept
TypeScript compiles more than the .ts files you wrote. It also reads declaration files (.d.ts) that describe JavaScript libraries and packages.
For example, @types/node describes Node.js APIs. Its declarations use built-in JavaScript types such as Map, Set, Symbol, WeakMap, and WeakSet. Those types were introduced with ES2015 (also called ES6).
When your configuration targets ES5, TypeScript may use only ES5's default standard-library declarations unless you explicitly choose additional libraries. As a result, TypeScript can read @types/node but fail because the ES2015 names that it refers to are unavailable.
exclude is often misunderstood:
- It stops TypeScript from automatically discovering matching files as root input files.
- It does not stop TypeScript from following dependencies.
- It does not reliably remove type declarations brought in through imports, triple-slash references, or automatically discovered
@typespackages.
This behavior is necessary: if a source file imports a package, TypeScript needs that package's type declarations to type-check the import correctly.
Mental Model
Think of include and exclude as the list of documents placed on a desk at the start of a research task.
If one of those documents cites another book, TypeScript may still open that book to understand the citation. exclude only says, “Do not place this book on the desk initially.” It does not say, “Never open this book if another document depends on it.”
The lib option is the built-in dictionary TypeScript can use. With only an ES5 dictionary, words such as Map and Symbol are missing. Adding ES2015 gives TypeScript the definitions it needs.
Syntax and Examples
Use lib to choose the built-in JavaScript and environment APIs available during type-checking.
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015", "dom"],
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules"]
}
What these options mean:
target: "es5"emits older ES5-compatible JavaScript syntax.lib: ["es2015", "dom"]makes ES2015 built-ins such asMap,Set, andSymbolavailable to the type checker, while also retaining browser types such aswindowanddocument.skipLibCheck: trueskips type checking inside.d.tsdeclaration files, including many files innode_modules.include: ["src/**/*.ts"]includes TypeScript files insrcand all of its nested directories.
If this is a Node-only project rather than a browser project, omit "dom":
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015"],
"skipLibCheck": true
},
"include": ["src/**/*.ts"]
}
skipLibCheck does not remove the declarations. TypeScript still uses them to understand package APIs; it simply avoids reporting errors found while checking the declaration files themselves.
Step by Step Execution
Consider this code:
// src/cache.ts
const cache = new Map<string, number>();
cache.set("visits", 1);
And this configuration:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015"]
},
"include": ["src/**/*.ts"]
}
TypeScript processes it roughly as follows:
- It finds
src/cache.tsbecause it matchessrc/**/*.ts. - It loads the ES2015 library declarations because of
"lib": ["es2015"]. - Those declarations define the generic
Map<K, V>type and its methods. - It checks that
"visits"is a string and1is a number. - Because the target is ES5, TypeScript emits ES5-style JavaScript where possible.
If lib did not include ES2015, step 2 would not provide the Map definition. The same missing-name issue can then appear in your own code or in @types/node.
If @types/node is installed, TypeScript can automatically include it. Its declarations refer to ES2015 APIs, so adding the ES2015 library resolves the underlying missing-name errors.
Real World Use Cases
- Older browser output: A web app may emit ES5 JavaScript for older environments while still using ES2015 type definitions. Runtime support may require polyfills.
- Node.js applications: Server code commonly installs
@types/nodefor types such asprocess,Buffer, and Node modules. Its declarations require suitable JavaScript library types. - Large dependency trees: Libraries may ship imperfect or version-mismatched
.d.tsfiles.skipLibCheckcan keep builds focused on application code. - Monorepos: Different packages can need different ambient types. The
typesoption prevents unrelated@typespackages from being included automatically. - Build performance: Skipping declaration-file checking can noticeably reduce checking time in projects with many dependencies.
Real Codebase Usage
A common application configuration uses a modern target and skips third-party declaration checking:
{
"compilerOptions": {
"target": "es2018",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
When a project needs Node types explicitly, teams often make that choice visible:
{
"compilerOptions": {
"types": ["node"],
"skipLibCheck": true
}
}
The types list is a whitelist for automatically included @types packages. For example, a browser-only project that should not receive Node globals can use:
{
"compilerOptions": {
"types": []
}
}
Use types: [] carefully. It can remove useful package-provided global types. It is not needed merely because a package exists in node_modules.
In maintained codebases, the preferred order is usually:
- Use compatible versions of TypeScript and
@typespackages. - Configure the correct
targetandlibvalues. - Enable
skipLibCheckif declaration checking is not valuable for the project. - Restrict
typesonly when unwanted global type packages are actually being loaded.
Common Mistakes
Assuming exclude blocks dependencies
This does not guarantee that Node declarations will never be loaded:
{
"exclude": ["node_modules"]
}
TypeScript can still load a declaration file when your program or an installed type package requires it. Use lib, types, or skipLibCheck based on the actual problem.
Adding many duplicate exclude patterns
These patterns are redundant:
"exclude": [
"node_modules",
"./node_modules",
"./node_modules/*"
]
Usually this is enough:
"exclude": ["node_modules"]
Using src/* when nested files should compile
src/* matches only direct children of src. It does not match src/features/login.ts.
"include": ["src/*"]
Prefer:
"include": ["src/**/*.ts"]
Treating skipLibCheck as a runtime polyfill
skipLibCheck only changes type checking. It does not create Map in an old browser. If your runtime lacks ES2015 APIs, provide an appropriate polyfill or choose a supported runtime.
Removing Node types when Node APIs are used
Setting "types": [] can hide Node globals such as process and Node module declarations. Do not use it if your application needs Node typings.
Using an incompatible old toolchain
TypeScript 2.9.2 is an old version. If possible, upgrade TypeScript and keep @types/node compatible with the TypeScript version and the Node.js version your project supports. When upgrading is not possible, use a compatible older @types/node release.
Comparisons
| Option | What it controls | Best use | What it does not do |
|---|---|---|---|
exclude | Files discovered as root project inputs | Avoid compiling generated folders or test output | Block imported or referenced declarations |
include | Files TypeScript initially searches for | Define the application's source folders | Control all dependency types |
lib | Built-in JavaScript and environment type definitions | Fix missing Map, Set, Promise, document, and similar names | Add runtime polyfills |
skipLibCheck | Checking inside .d.ts files | Avoid third-party declaration-file errors and speed up builds | Validate every dependency declaration |
types | Automatically included @types packages | Limit global type packages, such as Node or Jest | Stop types explicitly imported by source code |
target and lib are related but separate:
| Setting | Primary effect |
|---|---|
target | JavaScript syntax TypeScript emits |
lib | APIs TypeScript assumes exist for type checking |
It is valid to emit ES5 while using lib: ["es2015"], but your actual runtime must support ES2015 APIs or receive polyfills.
Cheat Sheet
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"lib": ["es2015", "dom"],
"skipLibCheck": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
- Missing
Map,Set,Symbol,WeakMap, orWeakSetusually means an ES2015 library definition is missing. - Add
"lib": ["es2015"]for Node projects. - Add
"dom"too for browser projects that usewindow,document, or DOM APIs. - Use
skipLibCheck: trueto skip errors inside.d.tsfiles. excludedoes not prevent TypeScript from loading declarations needed by dependencies.- Use
src/**/*.ts, notsrc/*, when source files can be in nested folders. - Use
types: ["node"]to explicitly include Node globals, ortypes: []to disable automatic@typesinclusion. - Type checking does not provide runtime support; add polyfills if the target runtime lacks an API.
FAQ
Why does TypeScript check files in node_modules even when they are excluded?
Because exclude affects initial file discovery, not dependency resolution. TypeScript still loads declarations needed to understand imported packages and automatically included type packages.
How do I fix Cannot find name 'Map' in TypeScript?
Add an ES2015-or-newer library to compilerOptions.lib, such as "lib": ["es2015", "dom"] for a browser project or "lib": ["es2015"] for a Node-only project.
Does skipLibCheck ignore all errors from node_modules?
It skips checking errors inside declaration files (.d.ts). It does not stop TypeScript from using those declarations, and your own code can still receive errors when it uses a library incorrectly.
Should I use skipLibCheck: true?
It is widely used in application projects to reduce build noise and improve speed. If you publish a TypeScript library or need maximum dependency type validation, consider leaving it off in at least one validation build.
Why is src/* not including my subfolders?
The * wildcard matches one path segment. Use src/**/*.ts to include .ts files under src recursively.
Can I target ES5 and use Map?
Yes, TypeScript can type-check Map with lib: ["es2015"] while emitting ES5 syntax. However, the runtime must provide Map, either natively or through a polyfill.
How can I stop @types/node from being automatically included?
Use the types compiler option. "types": [] disables automatic @types inclusion, while "types": ["node"] explicitly includes Node types. Do not disable Node types if your code needs Node APIs.
Is TypeScript 2.9.2 still a good version to use?
It is an older release. Upgrading TypeScript is recommended when project constraints allow it, especially when using current versions of @types packages.
Mini Project
Description
Configure a small TypeScript project that uses an ES2015 Map while emitting ES5 JavaScript. The project demonstrates that compiler output level, type-library availability, and runtime API support are separate concerns.
Goal
Create a working ES5-targeted TypeScript program that can type-check Map without errors from library declarations.
Requirements
Use target set to es5. Add the ES2015 type library through lib. Include TypeScript files recursively from src. Enable skipLibCheck to avoid checking third-party declaration files. Create a program that stores and reads a value using Map.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.