TypeScript 5.2 was announced on 24 August 2023. It supports using declarations from the explicit resource management proposal, which close a resource when the block that declared it ends, and they work in .js files as written.
It also lets methods like filter work on a union of array types, accepts .ts file paths in JSDoc import types, and starts rejecting configs where module and moduleResolution disagree about Node.js.
At a glance
| Release | TypeScript 5.2 |
|---|---|
| Released | 24 August 2023, the day of its announcement on the TypeScript blog |
| Source | TypeScript 5.2 release notes |
| Examples | Every example runs through TypeScript 5.9.3 when this site is built. |
| Previous release | TypeScript 5.1 |
| Next release | TypeScript 5.3 |
What changed for JavaScript
using declarations
using log = openLog("build") calls log[Symbol.dispose]() when the scope ends, however it ends. TypeScript needs the esnext.disposable lib (or esnext) for Symbol.dispose and the Disposable type; a .js file that runs as written also needs a runtime that supports using itself, since the polyfill for Symbol.dispose that the release notes suggest only helps output TypeScript rewrites.
/** @param {string} name */
function openLog(name) {
console.log(`open ${name}`);
return {
/** @param {string} line */
write(line) {
console.log(line);
},
[Symbol.dispose]() {
console.log(`close ${name}`);
},
};
}
export function build() {
using log = openLog("build");
log.write("step 1");
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": [],
"lib": [
"es2022",
"esnext.disposable",
"dom"
]
},
"include": [
"*.js"
]
} Methods on a union of arrays
Calling filter, find, some, every or reduce on string[] | number[] used to fail with "not callable". TypeScript now treats the union as (string | number)[] for the call.
/** @param {string[] | number[]} values */
export function present(values) {
return values.filter((value) => Boolean(value));
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} .ts paths in import types
A type-only import may name a .ts file by its real extension, and the release notes show the JSDoc form: @param {import("./types.ts").Order} order. A JavaScript project can then use types written in a .ts file without allowImportingTsExtensions.
/** @param {import("./types.ts").Order} order */
export function label(order) {
return `${order.id}: ${order.total.toFixed(2)}`;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} module and moduleResolution must match
When either module or moduleResolution is node16 or nodenext, the other must be the matching Node.js setting (TS5110). Setting "module": "nodenext" alone is enough, since it implies the resolution; module: esnext belongs with moduleResolution: bundler. This site's tests confirm the error with TypeScript 5.9.
Labeled and unlabeled tuple elements together
A tuple can label some elements and not others, [first: string, ...string[]], which was an error before. It works inside JSDoc braces.
What did not change
Nothing in 5.2 changes how JSDoc tags are read. A .js file that does not use the new syntax gets the same errors as before, apart from the configs that 5.2 now rejects and the calls on unions of arrays that now pass.
Upgrade notes
From the breaking changes:
- Fix mismatched
module/moduleResolutionpairs (TS5110). - Merged declarations in
.d.tsfiles must agree on whether they are exported.
Gotchas
usingacceptsnullandundefinedand skips them when the scope ends; any other value without a[Symbol.dispose]method is an error.
Sources
- TypeScript 5.2 release notes · TypeScript handbook
- Announcing TypeScript 5.2 · TypeScript blog
Last reviewed 20 October 2026.