lib & target
Two compiler options that sound similar but mean different things — target sets the JS version you emit; lib sets the types you can call.
target
Which JavaScript syntax tsc emits in the compiled output. Pick by what your deployment runtime supports.
| target | Where it runs |
|---|---|
ES2022 | Node 18+, all evergreen browsers. |
ES2020 | Older Safari support. |
ESNext | "Use everything new" — bundler handles down-leveling. |
ES5 | Legacy browsers — almost never needed anymore. |
lib
Which JS / DOM types TS knows about — affects what global APIs are visible, not what the runtime supports.
{
"compilerOptions": {
"target": "ES2022",
"lib": ["DOM", "DOM.Iterable", "ES2023"]
}
}
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2023"] // NO DOM
}
}
Why DOM matters
If "lib" includes "DOM", TS believes window, document, etc. exist. For Node code this is dangerous — typos will compile because of the matching browser symbols.
Default behaviour
If you don't set lib, TS picks one from target + a default of DOM + DOM.Iterable for browser-shaped targets. Explicit beats default.
Adding individual libs
{
"compilerOptions": {
"lib": ["ES2023", "DOM", "WebWorker"] // service worker app
}
}
Common pitfall
You see fetch is not defined at runtime — that's not a TS thing, that's the runtime missing a polyfill. lib only says "TS will type-check it"; you still need it to actually exist.
"DOM" from lib. The compiler will stop you from accidentally calling browser APIs in code that runs server-side.Example
// target: which JS syntax tsc emits (ES2022, ESNext)
// lib: which TS knows you can call (DOM, ES2023, ...)
//
// Browser app: target ES2020+, lib ['DOM','ES2022']
// Node 20+: target ES2022, lib ['ES2023']
console.log('Match target/lib to your deployment');
Try it Yourself »
Exercise
For a Node server you should drop this lib.
Three letters.
Discussion
Loading…