TypeScript release

TypeScript 5.0 for JavaScript users

  • Released 16 March 2023
  • Examples run with TypeScript 5.9

TypeScript 5.0 was announced on 16 March 2023. It brought two JSDoc tags that JavaScript projects had been asking for: @satisfies, which checks a value against a type without changing the value's own type, and @overload, which gives a function several call signatures.

It also added const type parameters, extends with a list of configs, moduleResolution: bundler with a set of resolution options, and verbatimModuleSyntax, and it deprecated a group of old options that 5.5 then stopped honoring.

At a glance

ReleaseTypeScript 5.0
Released16 March 2023, the day of its announcement on the TypeScript blog
SourceTypeScript 5.0 release notes
ExamplesEvery example runs through TypeScript 5.9.3 when this site is built.
Previous releaseTypeScript 4.9
Next releaseTypeScript 5.1

What changed for JavaScript

@satisfies

/** @satisfies {Routes} */ on a declaration, or before a parenthesized expression, checks the value against Routes and reports misspelled or missing properties, while the variable keeps the exact type of its value.

routes.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: routes.js(7,32): error TS2353: Object literal may only specify known properties, and 'atuh' does not exist in type 'Route'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. routes.js(7,32): error TS2353: Object literal may only specify known properties, and 'atuh' does not exist in type 'Route'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. routes.js(7,32): error TS2353: Object literal may only specify known properties, and 'atuh' does not exist in type 'Route'. checked 21 October 2026
/** @typedef {{ path: string, auth?: boolean }} Route */

/** @satisfies {Record<string, Route>} */
export const routes = {
  home: { path: "/" },
  account: { path: "/account", atuh: true },error TS2353: Object literal may only specify known properties, and 'atuh' does not exist in type 'Route'.
};
A misspelled property, caught by @satisfies.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

@overload

Each JSDoc comment with @overload declares one call signature for the function below it; the last comment, without the tag, types the implementation. Calls are checked against the overloads only.

catalog.js catalog.ts 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
/** @typedef {{ sku: string, name: string }} Product */

/** @type {Product[]} */
const products = [{ sku: "TEE-01", name: "Cotton tee" }];

/**
 * @overload
 * @param {string} sku
 * @returns {Product | undefined}
 */
/**
 * @overload
 * @param {string[]} skus
 * @returns {Product[]}
 */
/**
 * @param {string | string[]} skuOrSkus
 */
export function find(skuOrSkus) {
  if (Array.isArray(skuOrSkus)) return products.filter((p) => skuOrSkus.includes(p.sku));
  return products.find((p) => p.sku === skuOrSkus);
}

const one = find("TEE-01");
const many = find(["TEE-01", "MUG-07"]);
one?.name;
many.length;
type Product = { sku: string; name: string };

const products: Product[] = [{ sku: "TEE-01", name: "Cotton tee" }];

export function find(sku: string): Product | undefined;
export function find(skus: string[]): Product[];
export function find(skuOrSkus: string | string[]) {
  if (Array.isArray(skuOrSkus)) return products.filter((p) => skuOrSkus.includes(p.sku));
  return products.find((p) => p.sku === skuOrSkus);
}

const one = find("TEE-01");
const many = find(["TEE-01", "MUG-07"]);
one?.name;
many.length;
Two call signatures with @overload.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

const type parameters

@template const T makes TypeScript infer literal and tuple types for T from the argument, as if the caller had written as const. The release notes show the .ts form; this site checks the JSDoc form with TypeScript 5.9.

route.js route.ts 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
/**
 * @template const T
 * @param {T} routes
 * @returns {T}
 */
export function defineRoutes(routes) {
  return routes;
}

const routes = defineRoutes({ home: "/", cart: "/cart" });

/** @type {"/cart"} */
const cartPath = routes.cart;

export { cartPath };
export function defineRoutes<const T>(routes: T): T {
  return routes;
}

const routes = defineRoutes({ home: "/", cart: "/cart" });

const cartPath: "/cart" = routes.cart;

export { cartPath };
@template const keeps literal types.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

extends with several configs

extends takes a list. Later configs win where they set the same option, so a project can combine a shared base with a strictness preset.

cart.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: cart.js(2,23): error TS7006: Parameter 'lines' implicitly has an 'any' type. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. cart.js(2,23): error TS7006: Parameter 'lines' implicitly has an 'any' type. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. cart.js(2,23): error TS7006: Parameter 'lines' implicitly has an 'any' type. checked 21 October 2026
export function total(lines) {error TS7006: Parameter 'lines' implicitly has an 'any' type.
  return lines.length;
}
extends with two configs: the shared base, then strict on top.
jsconfig.json for this example
{
  "extends": [
    "./config/base.json",
    "./config/strict.json"
  ],
  "include": [
    "*.js"
  ]
}

moduleResolution bundler and the resolution options

bundler resolves imports the way Vite, esbuild and webpack do: relative imports without extensions are fine, and package exports are read with the import condition. The same release added allowArbitraryExtensions (types for imports like ./app.css), customConditions, resolvePackageJsonExports and resolvePackageJsonImports.

main.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
import { formatPrice } from "./format";

console.log(formatPrice(1999));
moduleResolution bundler: an import without its extension passes, because a bundler fills it in.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "preserve",
    "types": [],
    "moduleResolution": "bundler"
  },
  "include": [
    "*.js"
  ]
}

Comparing number | string with a number

>, <, >= and <= between number | string and a number are errors now (TS2365), because one side may be converted at run time. Convert explicitly with Number(limit) or +limit.

limit.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: limit.js(4,10): error TS2365: Operator '>' cannot be applied to types 'string | number' and 'number'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. limit.js(4,10): error TS2365: Operator '>' cannot be applied to types 'string | number' and 'number'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. limit.js(4,10): error TS2365: Operator '>' cannot be applied to types 'string | number' and 'number'. checked 21 October 2026
/** @param {number | string} limit */
export function overLimitLoose(limit) {
  return limit > 4;error TS2365: Operator '>' cannot be applied to types 'string | number' and 'number'.
}

/** @param {number | string} limit */
export function overLimit(limit) {
  return Number(limit) > 4;
}
> between number | string and a number is an error.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

What did not change

How TypeScript reads the JSDoc tags it already knew is unchanged; the new tags are additions.

  • verbatimModuleSyntax keeps imports and exports as written; in a .js file it mainly reports ES module syntax in a CommonJS file (TS1287).

Upgrade notes

From the breaking changes and deprecations:

  • forceConsistentCasingInFileNames now defaults to true, so an import whose casing differs from the file on disk is an error.
  • --newLine defaults to LF.
  • Deprecated: target: ES3, out, charset, importsNotUsedAsValues, preserveValueImports, keyofStringsOnly, suppressExcessPropertyErrors, suppressImplicitAnyIndexErrors, noImplicitUseStrict, noStrictGenericChecks and prepend. "ignoreDeprecations": "5.0" kept them working through 5.4.
  • TypeScript itself needs an ES2018 runtime (Node.js 10 or later).

Gotchas

  • @satisfies before an expression needs the parentheses: /** @satisfies {Routes} */ ({ ... }).

Sources

Last reviewed 20 October 2026.