TypeScript release

TypeScript 4.8 for JavaScript users

  • Released 25 August 2022
  • Examples run with TypeScript 5.9

TypeScript 4.8 was announced on 25 August 2022. One of its breaking changes is written for JavaScript files: a name that is only a type, such as a @typedef, can no longer be imported or exported by name in a .js file, because the import would fail when the code runs.

The release also reports comparisons with an object or array literal, which can never be true, and improves how unknown and unconstrained generics narrow under strictNullChecks.

At a glance

ReleaseTypeScript 4.8
Released25 August 2022, the day of its announcement on the TypeScript blog
SourceTypeScript 4.8 release notes
ExamplesEvery example runs through TypeScript 5.9.3 when this site is built.
Previous releaseTypeScript 4.7
Next releaseTypeScript 4.9

What changed for JavaScript

Types cannot be imported or exported by name in .js files

An ES module import of a name that has no value fails at run time, and before 4.8 TypeScript let a checked .js file write one: import { Product } from "./models.js", where Product is a @typedef. Under checkJs or // @ts-check that is now an error (TS18042), and so is an export { MyType } of a type-only name.

Two spellings work instead: an import type inside the braces, {import("./models.js").Product}, or, from TypeScript 5.5, the @import tag. To re-export a type, declare another @typedef for it; a @typedef is exported from its module already.

cart.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: cart.js(2,10): error TS18042: 'Product' is a type and cannot be imported in JavaScript files. Use 'import("./models.js").Product' in a JSDoc type annotation. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. cart.js(2,10): error TS18042: 'Product' is a type and cannot be imported in JavaScript files. Use 'import("./models.js").Product' in a JSDoc type annotation. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. cart.js(2,10): error TS18042: 'Product' is a type and cannot be imported in JavaScript files. Use 'import("./models.js").Product' in a JSDoc type annotation. checked 21 October 2026
import { Product } from "./models.js";error TS18042: 'Product' is a type and cannot be imported in JavaScript files. Use 'import("./models.js").Product' in a JSDoc type annotation.

/** @param {Product} product */
export function priceOf(product) {
  return product.price;
}
A @typedef cannot be imported by name in a .js file.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}
cart.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 { Product } from "./models.js" */

/** @param {import("./models.js").Product} product */
export function priceOf(product) {
  return product.price;
}

/** @param {Product} product */
export function skuOf(product) {
  return product.sku;
}
The two ways that work: an import() type, or @import from TypeScript 5.5.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

Comparing with an object or array literal

JavaScript compares objects by reference, so tags === [] is false for every array, empty or not. TypeScript 4.8 reports the comparison (TS2839). Check tags.length === 0 instead.

tags.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: tags.js(4,10): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. tags.js(4,10): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. tags.js(4,10): error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value. checked 21 October 2026
/** @param {string[]} tags */
export function isUntaggedLoose(tags) {
  return tags === [];error TS2839: This condition will always return 'false' since JavaScript compares objects by reference, not value.
}

/** @param {string[]} tags */
export function isUntagged(tags) {
  return tags.length === 0;
}
Comparing with [] is always false, and now reported.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

unknown and unconstrained type parameters

Under strictNullChecks, unknown now behaves like {} | null | undefined: a truthiness check narrows it to {}. The other side of the same change is stricter: a @template type parameter with no constraint may be null or undefined, so passing it where {} or object is expected is an error (TS2345). Constrain it with @template {{}} T, or check for null first.

keys.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: keys.js(12,17): error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. keys.js(12,17): error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. keys.js(12,17): error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'. checked 21 October 2026
/** @param {{}} value */
function keysOf(value) {
  return Object.keys(value);
}

/**
 * @template T
 * @param {T} input
 */
export function describeLoose(input) {
  return keysOf(input);error TS2345: Argument of type 'T' is not assignable to parameter of type '{}'.
}

/**
 * @template {{}} T
 * @param {T} input
 */
export function describe(input) {
  return keysOf(input);
}
An unconstrained type parameter is not assignable to {}.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": [
    "*.js"
  ]
}

Faster --watch, --build and --incremental

The release notes report 10 to 25 percent faster common operations in watch and build mode, and no-change rebuilds about 40 percent faster. File watching also survives a git checkout that replaces files, which used to leave stale errors on screen until tsc or the editor restarted.

What did not change

Importing values works as before, and a JSDoc import type (import("./x.js").Name) has worked since TypeScript 2.9. The new error only affects names that have no value.

Upgrade notes

Most new errors in checked JavaScript come from these three changes.

  • Replace imports of @typedef names with import types (or @import once you are on 5.5).
  • Constrain type parameters you pass to functions that take {} or object.
  • Error.cause is now typed unknown, so read it like a caught error.

Gotchas

  • A class is both a value and a type, so importing a class by name is still fine; the error is for names that exist only as types.

Sources

Last reviewed 20 October 2026.