Target and Lib in tsconfig

Target controls the JavaScript version TypeScript emits. Lib controls which built-in APIs are available for type-checking. Learn how they work together and how to choose the right values.

6 min read

Target and lib in tsconfig are two options that control what your code can do and where it can run. Target decides which version of JavaScript the compiler emits. Lib decides which built-in APIs TypeScript knows about during type-checking. They sound similar but solve completely different problems.

Getting them right means your output runs on your users' browsers and your editor has accurate autocomplete for every API you actually use.

The difference in one table

OptionWhat it controlsAffects
targetJavaScript syntax version in outputArrow functions, classes, async/await, optional chaining, template literals
libAvailable type definitions for built-in APIsPromise, Map, Set, Array.at(), document, window, fetch

Target affects the syntax of the emitted .js files. Lib affects what the compiler allows you to reference in your .ts files. They are independent: you can have modern API types with old syntax output, or modern syntax output with old API types.

target: controlling emitted syntax

The target option tells TypeScript how modern your output JavaScript should be. If you write an arrow function and target ES5, the compiler rewrites it as a regular function expression. If you target ES2015 or higher, the arrow passes through unchanged.

Here is the same TypeScript compiled with two different targets. Source:

typescripttypescript
const greet = (name: string) => `Hello, ${name}`;
class Person {
  constructor(public name: string) {}
}

With target set to ES5, the output uses older syntax compatible with any browser from the last decade. Arrow functions become regular functions, template literals become string concatenation, and classes become constructor functions with prototype assignments.

With target set to ES2022, the output is nearly identical to the source. Arrow functions, template literals, and classes all pass through because modern runtimes support them natively.

The full list of target values ranges from ES3 through ES2025, plus ESNext. For modern projects, ES2020 or ES2022 is a good baseline. Node.js 18 and all current browsers support ES2022 well.

lib: controlling known APIs

The lib option tells TypeScript which type definitions to load for built-in JavaScript and environment APIs. By default, lib is derived from target, but you can override it.

If you set target to ES2022 and omit lib, TypeScript includes type definitions for all ES2022 APIs: Array.at(), Error.cause(), Object.hasOwn(), top-level await, and more. You also get definitions for earlier versions like ES5 basics (Array, Object, Math).

For browser projects, you must add the DOM library. Without it, TypeScript does not know about document, window, HTMLElement, addEventListener, or any browser API. Your code that references document.querySelector will fail to compile even though it is valid browser JavaScript.

The most common lib values:

lib valueIncludes
ES2022Atomics, Array.at, Error.cause, Object.hasOwn, top-level await
DOMdocument, window, HTMLElement, fetch, addEventListener
DOM.IterableIterable DOM collections (NodeList, HTMLCollection in for...of)
WebWorkerself, postMessage, importScripts for Worker contexts
ESNextThe newest APIs your TypeScript version supports

You can combine multiple values in the array. A typical browser project uses ["ES2022", "DOM"], and optionally "DOM.Iterable" if you iterate over NodeList collections.

How target and lib interact

Setting target automatically sets lib to match, but you can override lib independently. This is useful when you have polyfills.

If you need to support an older browser but have a polyfill for Promise, you can set target to ES5 (for old syntax) and lib to ["ES2015", "DOM"] (to get Promise and other ES2015 API types). The compiler emits ES5-compatible syntax while still letting you write code that uses Promise.

Without the polyfill override, targeting ES5 with default lib would flag Promise as an unknown type. The lib setting bridges the gap between the syntax you emit and the APIs your runtime actually provides.

Choosing the right values

For a Node.js project, check the version you deploy to. Node.js 18 and 20 support ES2022. Set target to ES2022 and do not include DOM in lib. The default lib based on target is all you need.

For a browser project, start with target ES2020 or ES2022 and lib set to include DOM. If you use iterable DOM methods like forEach on a NodeList, also add DOM.Iterable:

jsonjson
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"]
  }
}

For a project that must support very old browsers but uses modern APIs through polyfills, set target low (ES5 or ES2015) and lib high (ES2022 plus DOM). You get modern type definitions while the compiler downlevels the syntax.

Avoid ESNext for production projects. It changes meaning between TypeScript versions, which can break your build unexpectedly during an upgrade. Pinning a specific version gives you predictable behavior.

For more on how these fit into your overall configuration, see important tsconfig options explained. To understand the module system side of the equation, read about the module option in tsconfig.

Rune AI

Rune AI

Key Insights

  • target controls which JS syntax features are downleveled (arrow functions, classes, etc.).
  • lib controls which built-in API types are available (Promise, Map, document, etc.).
  • You can set lib higher than target if you use polyfills.
  • For browser projects, always include DOM in lib.
  • Pinning a specific target like ES2022 is safer than using ESNext.
RunePowered by Rune AI

Frequently Asked Questions

Can I set lib to a higher version than target?

Yes. This is common when you use polyfills. If you have a Promise polyfill and target ES5, you can set lib to ES2015 or higher to get the Promise type definition while still emitting ES5-compatible JavaScript.

What happens if I do not set lib?

TypeScript automatically includes a default set of libraries based on target. For example, target ES2022 includes lib.es2022.d.ts. If you set lib explicitly, the default is replaced entirely, so include the ES library you need plus DOM if you run in a browser.

What is the difference between ESNext and a specific version?

ESNext always refers to the highest version your TypeScript installation supports, which changes between releases. Pinning a specific version like ES2022 gives you stable behavior across TypeScript upgrades.

Conclusion

Target and lib are two sides of the same coin. Target controls what JavaScript comes out of the compiler. Lib controls what APIs TypeScript knows about while checking your code. Set target to your oldest supported runtime, and set lib to include the APIs you have available through native support or polyfills.