TypeScript release

TypeScript 5.2 for JavaScript users

  • Released 24 August 2023
  • Examples run with TypeScript 5.9

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

ReleaseTypeScript 5.2
Released24 August 2023, the day of its announcement on the TypeScript blog
SourceTypeScript 5.2 release notes
ExamplesEvery example runs through TypeScript 5.9.3 when this site is built.
Previous releaseTypeScript 5.1
Next releaseTypeScript 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.

work.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @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");
}
using: the log is closed when the function returns.
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.

values.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @param {string[] | number[]} values */
export function present(values) {
  return values.filter((value) => Boolean(value));
}
filter on a string[] | number[] parameter.
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.

order.js TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @param {import("./types.ts").Order} order */
export function label(order) {
  return `${order.id}: ${order.total.toFixed(2)}`;
}
An import() type that names a .ts file.
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/moduleResolution pairs (TS5110).
  • Merged declarations in .d.ts files must agree on whether they are exported.

Gotchas

  • using accepts null and undefined and skips them when the scope ends; any other value without a [Symbol.dispose] method is an error.

Sources

Last reviewed 20 October 2026.