target names an ECMAScript version. When tsc writes output, it rewrites newer syntax down to that version. With noEmit nothing is rewritten, so in a JavaScript project target does one job: unless you set lib, it picks the built-in declarations, and so decides which methods exist. Promise.withResolvers and Object.groupBy are ES2024; with target: "es2022" they are TS2550.
Pick the newest year that every runtime you support implements in full. In TypeScript 5.9 a config without target gets es5, unless module is nodenext (which implies esnext) or node20 (es2023). TypeScript 6.0 changes the default to the newest year and deprecates es5.
At a glance
| Write | The ECMAScript year your oldest supported runtime fully implements, for example "es2022" or "es2024" |
|---|---|
| On the command line | --target |
| Type | string |
| Default in TypeScript 5.9 | es5 (module nodenext implies esnext, and node20 implies es2023) |
| Default from TypeScript 6.0 | the newest ECMAScript year (es2025 in 6.0); es5 is deprecated (TypeScript 6.0) |
| TypeScript's description | Set the JavaScript language version for emitted JavaScript and include compatible library declarations. |
| In the quick-start setups |
|
The description is quoted from TypeScript's own option list (Apache-2.0); see the notices. The setup values are the checked configs on the quick-start pages.
Examples
const { promise, resolve } = Promise.withResolvers();error TS2550: Property 'withResolvers' does not exist on type 'PromiseConstructor'. Do you need to change your target library? Try changing the 'lib' compiler option to 'es2024' or later.
setTimeout(() => resolve("done"), 100);
console.log(await promise);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} const { promise, resolve } = Promise.withResolvers();
setTimeout(() => resolve("done"), 100);
console.log(await promise);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2024",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} What target does not do with noEmit
It does not make old runtimes understand new syntax: your files run exactly as written. If you write ??= or a class field and set target: "es2017", nothing reports that an ES2017 engine cannot parse them, because tsc would rewrite them if it wrote output. A few features it cannot rewrite are errors, such as BigInt literals below es2020 (TS2737). Treat target as a statement about your runtime that picks the library, not as a syntax compatibility check.
target and lib together
Without lib, target also brings the browser (DOM) declarations. Setting lib replaces that whole default, so with lib set, target no longer chooses any declarations. A Node.js project that wants ES2024 methods and no DOM can write "lib": ["es2024"] and leave target to module: "nodenext".
Gotchas
target: "esnext"follows whatever TypeScript considers the newest year, so it can let through a method your runtime does not have yet. A fixed year is more honest about your runtime.
Sources
- TSConfig Reference: target · TypeScript
- TypeScript 5.7 release notes · TypeScript handbook
- TypeScript 5.9 release notes · TypeScript handbook
- TypeScript 6.0 release notes · TypeScript handbook
Last reviewed 20 October 2026.