TypeScript release

TypeScript 4.5 for JavaScript users

  • Released 17 November 2021
  • Examples run with TypeScript 5.9

TypeScript 4.5 was announced on 17 November 2021. It has a section written for JavaScript files: a const assertion can be written as a JSDoc cast, /** @type {const} */ (value), and a @template tag can give its type parameter a default.

The release also added the Awaited type, the module: es2022 setting for top-level await, and checks for #field in obj, a JavaScript feature for testing whether an object has a private field.

At a glance

ReleaseTypeScript 4.5
Released17 November 2021, the day of its announcement on the TypeScript blog
SourceTypeScript 4.5 release notes
ExamplesEvery example runs through TypeScript 5.9.3 when this site is built.
Previous releaseTypeScript 4.4
Next releaseTypeScript 4.6

What changed for JavaScript

const assertions in JSDoc

In a .ts file, as const keeps literal values and makes the result read-only. The JavaScript spelling is a cast to const: /** @type {const} */ ({ ... }), with the parentheses that every JSDoc cast needs.

routes.js routes.ts Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: routes.js(16,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. routes.ts(15,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. routes.js(16,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. routes.ts(15,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. routes.js(16,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. routes.ts(15,4): error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'. checked 21 October 2026
export const ROUTES = /** @type {const} */ ({
  home: "/",
  cart: "/cart",
  account: "/account",
});

/** @typedef {(typeof ROUTES)[keyof typeof ROUTES]} Path */

/** @param {Path} path */
export function go(path) {
  location.assign(path);
}

go(ROUTES.cart);
go("/checkout");error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'.
export const ROUTES = {
  home: "/",
  cart: "/cart",
  account: "/account",
} as const;

type Path = (typeof ROUTES)[keyof typeof ROUTES];

export function go(path: Path) {
  location.assign(path);
}

go(ROUTES.cart);
go("/checkout");error TS2345: Argument of type '"/checkout"' is not assignable to parameter of type 'Path'.
/** @type {const} */ keeps literal values as their own types.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Default type arguments in @template

A type parameter in square brackets with = gets a default: @template {string | number} [T=number]. The default applies when nothing else sets the type, as it does for type Box<T = number> in TypeScript.

result.js result.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 [T=unknown]
 * @typedef {{ ok: true, data: T } | { ok: false, error: string }} Result
 */

/** @type {Result<number>} */
const count = { ok: true, data: 3 };

/** @type {Result} */
const anything = { ok: true, data: "whatever" };

/** @param {Result<number>} result */
export function show(result) {
  return result.ok ? result.data.toFixed(0) : result.error;
}

show(count);
export { anything };
type Result<T = unknown> = { ok: true; data: T } | { ok: false; error: string };

const count: Result<number> = { ok: true, data: 3 };

const anything: Result = { ok: true, data: "whatever" };

export function show(result: Result<number>) {
  return result.ok ? result.data.toFixed(0) : result.error;
}

show(count);
export { anything };
A default type argument on a generic @typedef.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

The Awaited type

Awaited<T> is the type await produces: it unwraps promises, nested ones included. Promise.all and other built-ins use it, which fixes several inference problems with mixed promises and plain values.

account.js account.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
/** @param {string} id */
export async function loadAccount(id) {
  return { id, name: "Jane", orders: /** @type {string[]} */ ([]) };
}

/** @typedef {Awaited<ReturnType<typeof loadAccount>>} Account */

/** @param {Account} account */
export function greeting(account) {
  return `Hello, ${account.name}`;
}

loadAccount("u_1").then(greeting);
export async function loadAccount(id: string) {
  return { id, name: "Jane", orders: [] as string[] };
}

type Account = Awaited<ReturnType<typeof loadAccount>>;

export function greeting(account: Account) {
  return `Hello, ${account.name}`;
}

loadAccount("u_1").then(greeting);
Awaited<T>: the value a promise resolves to.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

#field in obj

JavaScript can test whether an object has a private field with #cents in other. TypeScript 4.5 checks the test and narrows other to the class, so other.#cents is allowed after it.

money.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
export class Money {
  #cents;

  /** @param {number} cents */
  constructor(cents) {
    this.#cents = cents;
  }

  /** @param {unknown} other */
  equals(other) {
    return typeof other === "object" && other !== null && #cents in other && this.#cents === other.#cents;
  }
}
#field in obj narrows to the class.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

Replacing the DOM types with a package

TypeScript looks for @typescript/lib-dom (and the other @typescript/lib-* names) in node_modules before using its own lib files, so a project can pin its DOM types with a package alias and upgrade TypeScript without new DOM errors. TypeScript 5.8 added libReplacement to switch the lookup off, and TypeScript 6.0 turns it off by default.

package.json
{
  "devDependencies": {
    "@typescript/lib-dom": "npm:@types/web"
  }
}

Import assertions

import data from "./data.json" assert { type: "json" } tells the runtime what kind of module to expect, and TypeScript 4.5 accepts it without checking the value. The syntax did not last: TypeScript 5.3 replaced it with import attributes (with), and 5.8 rejects assert under nodenext.

server.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: server.js(2,40): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. server.js(2,40): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. server.js(2,40): error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'. checked 21 October 2026
import settings from "./settings.json" assert { type: "json" };error TS2880: Import assertions have been replaced by import attributes. Use 'with' instead of 'assert'.

export const port = settings.port;
assert on a JSON import, under nodenext.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

What did not change

as const itself is still TypeScript syntax and an error in a .js file (TS8016); the JSDoc cast is the way to write it.

Upgrade notes

The breaking changes are small:

  • Inference that now goes through Awaited can produce different types in generic code.
  • A jsconfig.json or tsconfig.json with compiler options at the top level and no compilerOptions object is now an error, which catches options that were silently ignored.

Gotchas

  • A JSDoc cast needs parentheses around the value; /** @type {const} */ { a: 1 } without them is not a cast.

Sources

Last reviewed 20 October 2026.