TypeScript 5.5 was announced on 20 June 2024. It has the biggest JSDoc change of the 5.x line: the @import tag, which brings types from another module into a .js file with ES import syntax and imports nothing at run time.
It also infers type predicates from simple checks, so filter((x) => x !== undefined) narrows the array; narrows obj[key] when neither changes; checks the syntax of regular expression literals; and adds ${configDir} for shared config files.
At a glance
| Release | TypeScript 5.5 |
|---|---|
| Released | 20 June 2024, the day of its announcement on the TypeScript blog |
| Source | TypeScript 5.5 release notes |
| Examples | Every example runs through TypeScript 5.9.3 when this site is built. |
| Previous release | TypeScript 5.4 |
| Next release | TypeScript 5.6 |
What changed for JavaScript
The @import tag
/** @import { Product } from "./models.js" */ makes Product usable in the JSDoc of the file, as import type does in TypeScript. It replaces the workarounds: an import type at every use, or a @typedef alias for each one. The @import page has the forms it takes.
/** @import { Product, LineItem } from "./models.js" */
/** @import * as models from "./models.js" */
/**
* @param {Product} product
* @param {number} quantity
* @returns {LineItem}
*/
export function lineFor(product, quantity) {
return { sku: product.sku, quantity, total: product.price * quantity };
}
/** @type {models.Product} */
const tee = { sku: "TEE-01", name: "Cotton tee", price: 19.5 };
lineFor(tee, 2);import type { Product, LineItem } from "./models.js";
import type * as models from "./models.js";
export function lineFor(product: Product, quantity: number): LineItem {
return { sku: product.sku, quantity, total: product.price * quantity };
}
const tee: models.Product = { sku: "TEE-01", name: "Cotton tee", price: 19.5 };
lineFor(tee, 2);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Inferred type predicates
A function that returns a check on its parameter, such as (sku) => sku !== undefined, is now inferred as a type predicate, so filter with it returns string[] instead of (string | undefined)[]. Truthiness checks on numbers do not qualify, because 0 is falsy.
/** @type {(string | undefined)[]} */
const skus = ["TEE-01", undefined, "MUG-07"];
// The arrow function is inferred as (sku) => sku is string, so the result is string[].
const present = skus.filter((sku) => sku !== undefined);
present.map((sku) => sku.toLowerCase());
export { present };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} obj[key] narrows when both are constant
When neither the object nor the key is reassigned, typeof fields[key] === "string" narrows fields[key] inside the if. Before 5.5 you had to copy the value into a variable first.
/**
* @param {Record<string, unknown>} fields
* @param {string} key
*/
export function upper(fields, key) {
if (typeof fields[key] === "string") {
return fields[key].toUpperCase();
}
return "";
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} Regular expression literals are checked
TypeScript now checks the syntax of regular expression literals: an unescaped parenthesis, a backreference to a group that does not exist (TS1532 for a named one), or a feature newer than the target. Strings passed to new RegExp() are not checked.
export const isoDate = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
export const repeated = /(?<year>\d{4})-\k<month>/;error TS1532: There is no capturing group named 'month' in this regular expression.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": [
"*.js"
]
} ${configDir} in shared configs
A path in a config that others extend is resolved from the file it is written in. ${configDir} stands for the folder of the config being compiled instead, so a shared base can set outDir, typeRoots or declarationDir for every project that extends it. The release notes name jsconfig.json as well as tsconfig.json, and a one-off check with TypeScript 5.9 wrote declaration files to the extending project's own out folder.
{
"compilerOptions": {
"checkJs": true,
"strict": true,
// the folder of each jsconfig.json that extends this file
"typeRoots": ["${configDir}/types", "${configDir}/node_modules/@types"]
}
} What did not change
isolatedDeclarations, the release's option for fast declaration output, cannot be combined with allowJs (TS5053, checked with TypeScript 5.9), so it is for .ts sources only.
- Import types inside the braces (
import("./x.js").Name) still work and need no tag.
Upgrade notes
From the behavioral changes:
- The options deprecated in 5.0 (
charset,target: ES3,importsNotUsedAsValues,outand others) stop working; remove them from your config. - Inferred predicates can narrow an array more than before, so pushing
undefinedinto it later is an error; give the array an explicit@type. - Declaration output no longer adds
/// <reference types>lines by itself, which matters to libraries that write.d.tsfiles from JavaScript.
Gotchas
- Put each
@importin its own comment near the top of the file; a misspelled name is reported on that line, and every use of it then has an error type.
Sources
- TypeScript 5.5 release notes · TypeScript handbook
- Announcing TypeScript 5.5 · TypeScript blog
Last reviewed 20 October 2026.