{
  "versions": [
    {
      "id": "7-0",
      "slug": "7-0",
      "title": "TypeScript 7.0 for JavaScript users",
      "version": "7.0",
      "group": "TypeScript 6 and the native port",
      "released": "2026-07-08",
      "notes": "https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/",
      "checked": "notes",
      "summary": "TypeScript 7.0, the native port, for JavaScript: builds 8 to 12 times faster, and JSDoc is read like TypeScript, without @enum, @class or Closure types.",
      "intro": [
        "TypeScript 7.0 was announced on 8 July 2026. It is the native port: TypeScript's checker and tools rewritten in Go, installed from the same `typescript` package and run as the same `tsc`. The announcement reports full builds 8 to 12 times faster than 6.0, and much quicker errors in the editor.",
        "For JavaScript the port is more than a speed-up. Its checking of `.js` files was reworked to match `.ts` files, and several patterns from Closure Compiler and pre-class JavaScript are no longer understood: `@enum`, `@class` constructor functions, Closure function types, a value's name used as a type. This site's pages describe TypeScript 5.9, and every example on them also runs under 7.0.2 each time the site is built; this page reports the announcement and the port's list of changes, with what those runs show."
      ],
      "examples": [],
      "changes": [
        {
          "id": "native",
          "heading": "A native tsc",
          "body": [
            "Install `typescript` 7 and run `tsc` as before. Parsing, checking and writing output run in parallel; the experimental `--checkers` option sets how many type-checking threads run (4 by default), `--builders` does the same for project references, and `--singleThreaded` turns parallelism off. Watch mode was rebuilt on a new file watcher."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npm install --save-dev typescript@7\nnpx tsc -p jsconfig.json"
            }
          ]
        },
        {
          "id": "defaults",
          "heading": "The 6.0 defaults, and no deprecated options",
          "body": [
            "7.0 checks like [TypeScript 6.0](/typescript/6-0/) with `stableTypeOrdering` on: `strict` on, `types` empty, `module: esnext`, `noUncheckedSideEffectImports` on. The options 6.0 deprecated are now errors, among them `baseUrl`, `moduleResolution: node` and `classic`, `target: es5`, `esModuleInterop: false` and `alwaysStrict: false`. TypeScript 7.0.2 reports them as removed: TS5102 for a removed option, TS5108 for a removed value, such as `allowSyntheticDefaultImports: false` on the [esModuleInterop page](/config/es-module-interop/)."
          ]
        },
        {
          "id": "jsdoc",
          "heading": "JSDoc that 7.0 no longer reads",
          "body": [
            "The port's list of changes says JavaScript files are now analyzed like TypeScript files with a different syntax for types, and drops most Closure-specific support. For JSDoc that means:"
          ],
          "list": [
            "A value's name is not a type. `{defaults}` must become `{typeof defaults}` (TS2749 otherwise).",
            "`@enum` is an ordinary tag. Type the values with a `@typedef` over the object, as below.",
            "`@class` and `@constructor` no longer make a function a constructor, and constructor functions with `prototype` assignments are not modeled: write a `class`.",
            "Closure function types such as `{function(string): number}` are not parsed; write `{(s: string) => number}`.",
            "`{?}` is not a type (write `{any}`), and the postfix `!` is not supported.",
            "A JSDoc rest type, `{...number}`, means `number[]` and no longer makes a parameter a rest parameter; write `...values` in the parameter list. Using `arguments` no longer lets callers pass extra values either.",
            "Smaller changes from the same list: a `@param` tag no longer types more than one function, a `@type` cast no longer narrows the variable it is written on (as a cast in a `.ts` file does not), and `@overload` on an arrow function or function expression is ignored."
          ],
          "snippets": [
            {
              "file": "status.js",
              "lang": "js",
              "code": "export const Status = /** @type {const} */ ({ Draft: \"draft\", Live: \"live\" });\n\n/** @typedef {(typeof Status)[keyof typeof Status]} StatusValue */\n\n/** @param {StatusValue} status */\nexport function label(status) {\n  return status.toUpperCase();\n}"
            }
          ]
        },
        {
          "id": "commonjs",
          "heading": "CommonJS patterns",
          "body": [
            "The same list narrows what TypeScript reads in CommonJS files:"
          ],
          "list": [
            "A module uses either `module.exports = …` or `module.exports.name = …`, not both.",
            "`const mod = module.exports` aliases and `this.name = …` at the top level of a module are not read as exports; write `exports.name`.",
            "`require(\"fs\").readFile` is not followed; destructure instead: `const { readFile } = require(\"fs\")`.",
            "Nested exports such as `exports.a.b = 1` need `exports.a` declared first."
          ]
        },
        {
          "id": "declarations",
          "heading": "Declaration files written from JavaScript",
          "body": [
            "Declaration output and JavaScript parsing were largely rewritten, and the port's notes say `.d.ts` files written from `.js` files will differ from 6.0's; matching the old output exactly is not a goal. A library that ships types written from its JSDoc should compare its `.d.ts` files before releasing with 7.0. 6.0's `stableTypeOrdering` previews the new ordering."
          ]
        },
        {
          "id": "editors",
          "heading": "Editors and tools",
          "body": [
            "VS Code has a TypeScript 7 extension that becomes the default, with commands to switch between the 7 and 6 language servers; Visual Studio picks 7 per workspace; other editors talk to the new language server over LSP. 7.0 ships without a programmatic API, which 7.1 is meant to bring, so tools that call TypeScript as a library keep using 6.0 through the `@typescript/typescript6` package and its `tsc6` command. Editor support for Vue, Svelte, Astro, MDX and Angular templates still needs 6.0."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "The announcement says code that checks cleanly under 6.0, with `stableTypeOrdering` on and no `ignoreDeprecations`, should check the same under 7.0. Neither the announcement nor the port's list mentions `checkJs` or `jsconfig.json`; in this site's runs a `jsconfig.json` project keeps its file-name defaults under 7.0.2, as under 6.0.3. The common tags (`@param`, `@returns`, `@type`, `@typedef`, `@template`, `@satisfies`, `@import`, `@overload` on declarations) behave as in 5.9 in every example that uses them, apart from the classes counted below."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "Per the announcement and the port's notes:"
          ],
          "list": [
            "Move to 6.0 first and clear its deprecation errors; 7.0 removes those options.",
            "Turn on `stableTypeOrdering` in 6.0 to see 7.0's type ordering, especially in written `.d.ts` files.",
            "Rewrite Closure types, `@enum` objects and constructor functions before switching; each has a form that works in 5.9 as well.",
            "Keep 6.0 installed beside 7.0 for tools that need TypeScript's API, such as typescript-eslint, until they support the new one."
          ]
        }
      ],
      "gotchas": [
        "This site's pages describe TypeScript 5.9. Every example also runs under 7.0, and where 7.0.2 reports something else, the example's badge shows ✗ and its page explains why in a section \"Under TypeScript 6.0 and 7.0\"."
      ],
      "related": [
        "typescript/6-0",
        "tags/enum",
        "tags/class",
        "types/closure-syntax",
        "types/function-types",
        "editors/vs-code"
      ],
      "sources": [
        {
          "title": "Announcing TypeScript 7.0",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-7-0/",
          "publisher": "TypeScript blog"
        },
        {
          "title": "CHANGES.md: changes between TypeScript 6 and the native port",
          "url": "https://github.com/microsoft/typescript-go/blob/main/CHANGES.md",
          "publisher": "microsoft/typescript-go on GitHub"
        },
        {
          "title": "Announcing TypeScript 6.0",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-21"
    },
    {
      "id": "6-0",
      "slug": "6-0",
      "title": "TypeScript 6.0 for JavaScript users",
      "version": "6.0",
      "group": "TypeScript 6 and the native port",
      "released": "2026-03-23",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html",
      "checked": "notes",
      "summary": "TypeScript 6.0 for JavaScript: strict on and types empty by default, baseUrl and moduleResolution node deprecated, and the step before 7.0.",
      "intro": [
        "TypeScript 6.0 was announced on 23 March 2026. It is the last release built on TypeScript's original codebase and the step before [7.0, the native port](/typescript/7-0/): it changes several defaults to what most projects set anyway, and deprecates options that 7.0 removes. A deprecated option is an error unless the config says `\"ignoreDeprecations\": \"6.0\"`.",
        "For a JavaScript project the defaults matter most. A `jsconfig.json` that does not set `strict` or `types` checks differently under 6.0. This site's pages describe 5.9, and every example on them also runs under 6.0.3 each time the site is built; this page reports the release notes, with what those runs show."
      ],
      "examples": [],
      "changes": [
        {
          "id": "defaults",
          "heading": "New defaults",
          "body": [
            "Per the release notes: [strict](/config/strict/) is on, [types](/config/types/) is `[]` (no `@types` package is loaded unless listed), [module](/config/module/) is `esnext`, [target](/config/target/) is the newest ECMAScript year (`es2025`), [noUncheckedSideEffectImports](/config/no-unchecked-side-effect-imports/) is on, `libReplacement` is off, and `rootDir` is the folder of the config. Each of those config pages has a \"Default from TypeScript 6.0\" row.",
            "The notes do not mention `jsconfig.json`. In a one-off run on 20 October 2026, a `jsconfig.json` with only `checkJs` reported an untyped parameter (TS7006) and a missing global from an `@types` package (TS2304) under 6.0.3, where 5.9 reported neither; `--showConfig` still listed the defaults a file named `jsconfig.json` gets (`allowJs`, `maxNodeModuleJsDepth: 2`, `skipLibCheck`, `noEmit` and `allowSyntheticDefaultImports`). Setting the options you rely on keeps a config the same across versions:"
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"strict\": true,\n    \"module\": \"nodenext\",\n    // list the globals you use; 6.0 loads none by default\n    \"types\": [\"node\"]\n  },\n  \"include\": [\"src\"]\n}"
            }
          ]
        },
        {
          "id": "deprecated",
          "heading": "Deprecated options",
          "body": [
            "These now report a deprecation error and are removed in 7.0: `target: es5`, `downlevelIteration`, `moduleResolution: node` (also called `node10`) and `classic`, `module` set to `amd`, `umd`, `system` or `none`, `baseUrl`, `esModuleInterop` or `allowSyntheticDefaultImports` set to `false`, `alwaysStrict: false`, `outFile`, `assert` on imports, and the `no-default-lib` reference directive. TypeScript 6.0.3 reports them as TS5101 for an option and TS5107 for a value, such as `allowSyntheticDefaultImports: false` on the [esModuleInterop page](/config/es-module-interop/).",
            "Two are common in JavaScript projects. Older `jsconfig.json` files often set `baseUrl` so that `paths` work; under 6.0, `baseUrl` is not a lookup root, so write the folder into each path instead. And `moduleResolution: node` should become `nodenext` for code Node.js runs directly, or `bundler` for bundled code."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    // before: \"baseUrl\": \".\", \"paths\": { \"@app/*\": [\"src/*\"] }\n    \"paths\": { \"@app/*\": [\"./src/*\"] }\n  }\n}"
            }
          ]
        },
        {
          "id": "command-line-files",
          "heading": "Files on the command line beside a tsconfig.json",
          "body": [
            "Running `tsc src/main.js` in a folder with a `tsconfig.json` is now an error (TS5112), because the config would be ignored without a word; `--ignoreConfig` makes the choice explicit. The notes name `tsconfig.json` only, and in the one-off run 6.0.3 did not report TS5112 in a folder that had only a `jsconfig.json`. Running `tsc -p jsconfig.json` is unaffected."
          ]
        },
        {
          "id": "libs",
          "heading": "Lib changes",
          "body": [
            "`\"lib\": [\"dom\"]` now includes what `dom.iterable` and `dom.asynciterable` used to add, so `for (const el of document.querySelectorAll(\"li\"))` needs no second entry. The new `es2025` target and lib add `RegExp.escape` and move `Promise.try`, the iterator helpers and the new `Set` methods out of `esnext`. `esnext` gains the types for `Temporal` and for `Map` and `WeakMap`'s `getOrInsert` and `getOrInsertComputed`."
          ]
        },
        {
          "id": "subpath-imports",
          "heading": "Subpath imports that start with #/",
          "body": [
            "Node.js allows `#/` as the prefix of a subpath import, and 6.0 resolves it under `moduleResolution: nodenext` and `bundler`. The `imports` field of `package.json` is Node.js's own alternative to `paths`, so it needs no `jsconfig.json` setting at all."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"type\": \"module\",\n  \"imports\": {\n    \"#/*\": \"./src/*\"\n  }\n}"
            }
          ]
        },
        {
          "id": "inference",
          "heading": "Inference for methods that do not use this",
          "body": [
            "An object literal with methods, passed to a generic function, used to infer differently depending on whether a callback was written as a method or an arrow function and on the order of the properties. A method that never uses `this` is now treated like an arrow function, so the order no longer matters. JavaScript code that passes option objects with methods to typed libraries benefits."
          ]
        },
        {
          "id": "stable-ordering",
          "heading": "stableTypeOrdering",
          "body": [
            "A new flag orders union members and properties the way 7.0 does, which can change the order in written `.d.ts` files and, rarely, which errors appear. The notes describe it as a migration aid that can slow checking by up to about 25 percent, not a setting to keep on."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "The release notes have no section on JSDoc or JavaScript files: the JSDoc changes come with 7.0. 6.0 keeps 5.9's API, so editors and tools that use TypeScript as a library keep working. In this site's runs, each example that reports something different under 6.0.3 sets, or leaves out, an option whose default changed or that 6.0 deprecates, apart from one: an unresolved side-effect import is now TS2882 rather than TS2307. The section below counts them."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "Per the release notes, most projects need one or two of these:"
          ],
          "list": [
            "Set `types` to the global packages you use (`[\"node\"]`, plus a test runner's types); `[\"*\"]` restores loading every `@types` package.",
            "Set `strict` explicitly if your project does not want it.",
            "Set `rootDir` if your output used to land beside an inferred source root.",
            "Replace `baseUrl` and `moduleResolution: node` before 7.0 removes them; `\"ignoreDeprecations\": \"6.0\"` buys time, not a fix.",
            "Replace `assert { type: \"json\" }` with `with { type: \"json\" }`."
          ]
        }
      ],
      "gotchas": [
        "`ignoreDeprecations` takes the version whose deprecations you are silencing: `\"6.0\"` here, as `\"5.0\"` was for the 5.0 deprecations."
      ],
      "related": [
        "typescript/7-0",
        "typescript/5-9",
        "config/strict",
        "config/types",
        "config/module-resolution",
        "config/paths"
      ],
      "sources": [
        {
          "title": "TypeScript 6.0 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-6-0.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 6.0",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-21"
    },
    {
      "id": "5-9",
      "slug": "5-9",
      "title": "TypeScript 5.9 for JavaScript users",
      "version": "5.9",
      "group": "TypeScript 5",
      "released": "2025-08-01",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-9.html",
      "checked": "examples",
      "summary": "TypeScript 5.9 for checked JavaScript: a shorter tsc --init, import defer, module node20, and a Uint8Array is no longer an ArrayBuffer.",
      "intro": [
        "TypeScript 5.9 was announced on 1 August 2025, and it is the version this site checks every example with. For JavaScript projects it brought a much shorter `tsc --init`, the `import defer` syntax, a stable `module: node20` setting and longer, more useful hovers in editors.",
        "Its main breaking change is in the built-in types: `ArrayBuffer` is no longer a supertype of the typed arrays, so a `Uint8Array` or a Node.js `Buffer` passed where an `ArrayBuffer` is expected is now an error."
      ],
      "examples": [],
      "changes": [
        {
          "id": "tsc-init",
          "heading": "A shorter tsc --init",
          "body": [
            "`tsc --init` writes a short `tsconfig.json` with current settings instead of a long commented list: `module: nodenext`, `target: esnext`, `types: []`, `strict` and several stricter checks. It does not set `allowJs` or `checkJs`; this site's [quick start](/start/) shows how to turn its output into a `jsconfig.json`."
          ],
          "examples": [
            "start/tsc-init"
          ]
        },
        {
          "id": "import-defer",
          "heading": "import defer",
          "body": [
            "`import defer * as report from \"./report.js\"` loads a module without running it; its code runs the first time one of its exports is read. Only namespace imports can be deferred, and TypeScript accepts the syntax only with `module: preserve` or `esnext`, because it does not rewrite it. Your runtime or bundler has to support it."
          ],
          "examples": [
            "typescript/5-9-import-defer"
          ]
        },
        {
          "id": "node20",
          "heading": "module node20",
          "body": [
            "`\"module\": \"node20\"` models Node.js 20 and, unlike `nodenext`, will not change in later releases. It implies `target: es2023`, where `nodenext` implies `esnext`."
          ]
        },
        {
          "id": "hovers",
          "heading": "Longer hovers and DOM summaries",
          "body": [
            "Hovers on DOM APIs now include a short summary from MDN, hovers can be expanded to show the types inside a type (a preview in VS Code), and the maximum hover length is larger and configurable (`js/ts.hover.maximumLength` in VS Code). All of it works in `.js` files, where hovers are how you read the types JSDoc gives your code."
          ]
        },
        {
          "id": "array-buffer",
          "heading": "A Uint8Array is not an ArrayBuffer",
          "body": [
            "The built-in types changed so that `ArrayBuffer` is no longer a supertype of `Uint8Array` and the other typed arrays, nor of Node.js `Buffer`. Code that passes a typed array where an `ArrayBuffer` is declared now fails (TS2345). Pass `bytes.buffer`, or declare the parameter as `Uint8Array`. Errors that mention `Buffer` usually mean `@types/node` needs updating."
          ],
          "examples": [
            "typescript/5-9-array-buffer"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "No JSDoc tag changed in 5.9. The release notes do not mention JavaScript files, and every example on this site runs on 5.9 as written."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the behavioral changes:"
          ],
          "list": [
            "Typed arrays passed as `ArrayBuffer`: use `.buffer` or a typed-array parameter, and update `@types/node`.",
            "Type argument inference changed in some generic calls; a JSDoc cast or a more specific `@param` type settles them, since `.js` files cannot pass type arguments.",
            "`tsc --init` output is different, so scripts that edit it may need changes."
          ]
        }
      ],
      "gotchas": [
        "`new Uint8Array([1, 2, 3])` is a `Uint8Array<ArrayBuffer>`, so its `.buffer` is an `ArrayBuffer`; a typed array of unknown origin is `Uint8Array<ArrayBufferLike>`, whose `.buffer` may be a `SharedArrayBuffer`."
      ],
      "related": [
        "start/node-esm",
        "config/module",
        "config/target",
        "editors/vs-code",
        "typescript/5-8",
        "typescript/6-0"
      ],
      "sources": [
        {
          "title": "TypeScript 5.9 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-9.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.9",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-8",
      "slug": "5-8",
      "title": "TypeScript 5.8 for JavaScript users",
      "version": "5.8",
      "group": "TypeScript 5",
      "released": "2025-02-28",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html",
      "checked": "examples",
      "summary": "TypeScript 5.8 for checked JavaScript: each branch of a returned conditional is checked, require() of ES modules under nodenext, and no more assert.",
      "intro": [
        "TypeScript 5.8 was announced on 28 February 2025. In checked JavaScript the change most people meet is a stricter return check: each branch of a conditional in a `return` statement is compared with the declared `@returns` type, so an `any` in one branch no longer hides a wrong value in the other.",
        "For Node.js projects it follows Node.js 22: a CommonJS file may `require()` an ES module under `module: nodenext`, a new stable `module: node18` keeps the older rules, and the old `assert` import syntax is an error under `nodenext`."
      ],
      "examples": [],
      "changes": [
        {
          "id": "return-branches",
          "heading": "Each branch of a returned conditional is checked",
          "body": [
            "In `return cache.has(href) ? cache.get(href) : href`, the first branch is `any` (`cache` is a `Map<string, any>`), and before 5.8 the whole conditional became `any`, so returning a string from a function declared `@returns {Link}` passed. Now each branch is checked against the return type on its own (TS2322 on `href`)."
          ],
          "examples": [
            "typescript/5-8-return-branches"
          ]
        },
        {
          "id": "require-esm",
          "heading": "require() of an ES module under nodenext",
          "body": [
            "Node.js 22 lets CommonJS code `require()` an ES module (one without top-level `await`). Under `module: nodenext`, TypeScript 5.8 accepts it too, so a `.cjs` file, or a `.js` file in a package without `\"type\": \"module\"`, can require an ESM-only package. This site's tests confirm with TypeScript 5.9 that the same `require` is TS1479 under `node16`."
          ],
          "examples": [
            "typescript/5-8-require-esm"
          ]
        },
        {
          "id": "node18",
          "heading": "module node18",
          "body": [
            "A stable setting for projects that stay on Node.js 18: it does not allow `require()` of ES modules, and it still accepts `assert` on imports."
          ]
        },
        {
          "id": "assert",
          "heading": "assert on imports is an error under nodenext",
          "body": [
            "Import assertions (`assert { type: \"json\" }`) were replaced by import attributes (`with { type: \"json\" }`), and Node.js 22 no longer accepts `assert`. Under `nodenext`, TypeScript 5.8 reports it ([TS2880](/errors/ts2880/))."
          ],
          "examples": [
            "typescript/5-8-import-assert"
          ]
        },
        {
          "id": "lib-replacement",
          "heading": "libReplacement",
          "body": [
            "TypeScript looks for packages like `@typescript/lib-dom` to replace its lib files (see [4.5](/typescript/4-5/)). `\"libReplacement\": false` turns the lookup, and the watching of `node_modules` that comes with it, off. [TypeScript 6.0](/typescript/6-0/) makes `false` the default."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`erasableSyntaxOnly`, the other new option, reports TypeScript-only syntax such as `enum` and parameter properties. JavaScript cannot contain that syntax, so it changes nothing in a `.js` file."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the release notes:"
          ],
          "list": [
            "Replace `assert` with `with` on imports under `nodenext`.",
            "Expect new TS2322 errors where one branch of a returned conditional was `any`.",
            "The DOM types changed again."
          ]
        }
      ],
      "gotchas": [
        "Under `nodenext`, a JSON import from an ES module also needs `with { type: \"json\" }` ([TS1543](/errors/ts1543/), since 5.7)."
      ],
      "related": [
        "config/module",
        "config/resolve-json-module",
        "tags/returns",
        "errors/ts2322",
        "typescript/5-7",
        "typescript/5-9"
      ],
      "sources": [
        {
          "title": "TypeScript 5.8 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-8.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.8",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-8/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-7",
      "slug": "5-7",
      "title": "TypeScript 5.7 for JavaScript users",
      "version": "5.7",
      "group": "TypeScript 5",
      "released": "2024-11-22",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-7.html",
      "checked": "examples",
      "summary": "TypeScript 5.7 for checked JavaScript: variables nothing ever assigns, JSON imports need with { type: \"json\" } under nodenext, and es2024.",
      "intro": [
        "TypeScript 5.7 was announced on 22 November 2024. In checked JavaScript it reports a variable that is read inside a function but never assigned anywhere, and under `module: nodenext` it checks JSON imports the way Node.js loads them: with an import attribute, and through the default export only.",
        "It also added `target` and `lib` `es2024` (`Object.groupBy`, `Promise.withResolvers`), let editors find the right config file further up the folder tree, and sped up tsc's start with Node.js's compile cache."
      ],
      "examples": [],
      "changes": [
        {
          "id": "never-assigned",
          "heading": "Variables that are never assigned",
          "body": [
            "TypeScript already reported a variable read before assignment in straight-line code. 5.7 also reports one read inside a function when no code anywhere assigns it (TS2454). A `let` with a `@type` and no initializer is the usual case."
          ],
          "examples": [
            "typescript/5-7-never-assigned"
          ]
        },
        {
          "id": "json",
          "heading": "JSON imports under nodenext",
          "body": [
            "Under `module: nodenext`, importing a `.json` file from an ES module needs `with { type: \"json\" }` ([TS1543](/errors/ts1543/) without it), and the data is available only as the default export, not as named exports. That is how Node.js loads JSON."
          ],
          "examples": [
            "config/resolve-json-module-attribute"
          ]
        },
        {
          "id": "es2024",
          "heading": "target and lib es2024",
          "body": [
            "`es2024` brings the types for `Object.groupBy`, `Map.groupBy`, `Promise.withResolvers` and the resizable `ArrayBuffer`. A `target` older than that leaves them out unless `lib` adds them."
          ],
          "examples": [
            "config/target-es2024"
          ]
        },
        {
          "id": "editor-config",
          "heading": "Editors search further for a config file",
          "body": [
            "An editor used to give a file to the first `tsconfig.json` or `jsconfig.json` it found walking up from the file, even if that config did not include it. 5.7 keeps looking for a config that does, which helps folders with their own config for tests or scripts."
          ]
        },
        {
          "id": "compile-cache",
          "heading": "Faster start with Node.js compile caching",
          "body": [
            "On Node.js 22 and later, tsc uses `module.enableCompileCache()`, so its own code is parsed once and reused between runs. The release notes measured `tsc --version` at about 2.5 times faster; every run of `npm run lint` starts a little sooner."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`rewriteRelativeImportExtensions`, the release's new emit option, rewrites `.ts` import paths in output. JavaScript files import `.js` paths already, so it does not apply to them."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the behavioral changes:"
          ],
          "list": [
            "Add `with { type: \"json\" }` to JSON imports in ES modules under `nodenext`, and read the data through the default import.",
            "Typed arrays are generic over their buffer (`Uint8Array<ArrayBufferLike>`); errors about `Buffer` usually mean `@types/node` is out of date.",
            "Under `noImplicitAny` without `strictNullChecks`, a callback that returns `null` where a generic return type is expected is now an implicit any (TS7011)."
          ]
        }
      ],
      "gotchas": [
        "TS2454 for a never-assigned variable is reported where it is read, not where it is declared; the fix is usually an initializer."
      ],
      "related": [
        "errors/ts2454",
        "config/resolve-json-module",
        "config/target",
        "config/lib",
        "typescript/5-6",
        "typescript/5-8"
      ],
      "sources": [
        {
          "title": "TypeScript 5.7 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-7.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.7",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-7/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-6",
      "slug": "5-6",
      "title": "TypeScript 5.6 for JavaScript users",
      "version": "5.6",
      "group": "TypeScript 5",
      "released": "2024-09-09",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html",
      "checked": "examples",
      "summary": "TypeScript 5.6 for checked JavaScript: conditions that are always truthy are errors, iterator results under strict, and noUncheckedSideEffectImports.",
      "intro": [
        "TypeScript 5.6 was announced on 9 September 2024. It reports conditions that can only go one way, such as a regular expression used as an `if` condition instead of calling `.test()`, and under `strict` it types the value of a finished built-in iterator as `undefined`, not `any`.",
        "It added `noUncheckedSideEffectImports` for misspelled side-effect imports, `--noCheck` for writing output without checking, and editor changes that report errors for the visible part of a file first."
      ],
      "examples": [],
      "changes": [
        {
          "id": "truthy-checks",
          "heading": "Conditions that are always truthy or nullish",
          "body": [
            "A condition TypeScript can tell from its syntax is always truthy is an error (TS2872): a regular expression literal without `.test()`, an arrow function written where `>=` was meant, an object literal. A `??` whose left side can never be nullish is reported too. `true`, `false`, `0` and `1` are allowed, so `while (true)` stays fine."
          ],
          "examples": [
            "typescript/5-6-always-truthy"
          ]
        },
        {
          "id": "iterator-return",
          "heading": "Built-in iterators under strict",
          "body": [
            "The value of an iterator result before you check `done` used to be `any` for built-in iterators. A new option in the `strict` family, `strictBuiltinIteratorReturn`, makes it `undefined`, so `map.keys().next().value` is `string | undefined` and has to be checked (TS18048)."
          ],
          "examples": [
            "typescript/5-6-iterator-return"
          ]
        },
        {
          "id": "side-effect-imports",
          "heading": "noUncheckedSideEffectImports",
          "body": [
            "An import with no bindings, `import \"./polyfills.js\"`, was never resolved, so a misspelled path passed. With the new option on, it has to resolve. [TypeScript 6.0](/typescript/6-0/) turns it on by default."
          ],
          "examples": [
            "config/no-unchecked-side-effect-imports-on"
          ]
        },
        {
          "id": "no-check",
          "heading": "--noCheck",
          "body": [
            "`--noCheck` writes output without type-checking. For a JavaScript library that writes `.d.ts` files from its JSDoc, it separates the two jobs: write declarations with `--noCheck`, and check with `tsc -p jsconfig.json` as usual."
          ]
        },
        {
          "id": "editor-regions",
          "heading": "Errors for the visible part of the file first",
          "body": [
            "Editors can ask for the errors in the region on screen before the whole file, which the release notes show cutting the first response in a very large file from seconds to well under a second."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "Iterator helper methods (`map`, `filter`, `take` on iterators) were added to the `esnext` lib in this release; a project that targets an older lib does not see them."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the behavioral changes:"
          ],
          "list": [
            "New TS2872 and related errors for conditions that cannot change.",
            "Under `strict`, iterator results need a `done` check (or `strictBuiltinIteratorReturn: false`).",
            "`tsc --build` always writes a `.tsbuildinfo` file.",
            "The `\"type\"` field of a package inside `node_modules` is respected under every `module` setting, which can change how a dependency's types are read."
          ]
        }
      ],
      "gotchas": [
        "Destructure both `value` and `done` from `next()`, then check `done`; the check narrows `value` to the element type."
      ],
      "related": [
        "config/no-unchecked-side-effect-imports",
        "config/strict",
        "errors/ts18048",
        "typescript/5-5",
        "typescript/5-7"
      ],
      "sources": [
        {
          "title": "TypeScript 5.6 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-6.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.6",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-5",
      "slug": "5-5",
      "title": "TypeScript 5.5 for JavaScript users",
      "version": "5.5",
      "group": "TypeScript 5",
      "released": "2024-06-20",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html",
      "checked": "examples",
      "summary": "TypeScript 5.5 for checked JavaScript: the JSDoc @import tag, inferred type predicates, regular expression checks, and ${configDir}.",
      "intro": [
        "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."
      ],
      "examples": [],
      "changes": [
        {
          "id": "import-tag",
          "heading": "The @import tag",
          "body": [
            "`/** @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](/tags/import/) has the forms it takes."
          ],
          "examples": [
            "tags/import-named"
          ]
        },
        {
          "id": "inferred-predicates",
          "heading": "Inferred type predicates",
          "body": [
            "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."
          ],
          "examples": [
            "types/predicate-inferred"
          ]
        },
        {
          "id": "constant-index",
          "heading": "obj[key] narrows when both are constant",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/5-5-constant-index"
          ]
        },
        {
          "id": "regex",
          "heading": "Regular expression literals are checked",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/5-5-regex-check"
          ]
        },
        {
          "id": "config-dir",
          "heading": "${configDir} in shared configs",
          "body": [
            "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."
          ],
          "snippets": [
            {
              "file": "config/base.json",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"strict\": true,\n    // the folder of each jsconfig.json that extends this file\n    \"typeRoots\": [\"${configDir}/types\", \"${configDir}/node_modules/@types\"]\n  }\n}"
            }
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`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."
          ],
          "list": [
            "Import types inside the braces (`import(\"./x.js\").Name`) still work and need no tag."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the behavioral changes:"
          ],
          "list": [
            "The options deprecated in 5.0 (`charset`, `target: ES3`, `importsNotUsedAsValues`, `out` and others) stop working; remove them from your config.",
            "Inferred predicates can narrow an array more than before, so pushing `undefined` into 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.ts` files from JavaScript."
          ]
        }
      ],
      "gotchas": [
        "Put each `@import` in 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."
      ],
      "related": [
        "tags/import",
        "types/type-predicates",
        "types/import-types",
        "config/extends",
        "typescript/5-4",
        "typescript/5-6"
      ],
      "sources": [
        {
          "title": "TypeScript 5.5 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-5.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.5",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-4",
      "slug": "5-4",
      "title": "TypeScript 5.4 for JavaScript users",
      "version": "5.4",
      "group": "TypeScript 5",
      "released": "2024-03-06",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html",
      "checked": "examples",
      "summary": "TypeScript 5.4 for checked JavaScript: narrowing that survives into callbacks, the NoInfer type, Object.groupBy types, and module preserve.",
      "intro": [
        "TypeScript 5.4 was announced on 6 March 2024. Its most useful change for JavaScript is in narrowing: a parameter or `let` that has been narrowed keeps its narrowed type inside a callback created after its last assignment, which removes many \"possibly undefined\" errors in code that uses `map` and `forEach`.",
        "It also added the `NoInfer` utility type, types for `Object.groupBy` and `Map.groupBy`, and `module: preserve`, which models bundlers that accept both `import` and `require()`."
      ],
      "examples": [],
      "changes": [
        {
          "id": "closure-narrowing",
          "heading": "Narrowing inside callbacks",
          "body": [
            "After `if (typeof tags === \"string\") tags = tags.split(\",\")`, `tags` is a `string[]`, but a callback that read it used to see `string | string[]` again, because the callback might run later. Now TypeScript keeps the narrowing when the callback is created after the last assignment to the variable."
          ],
          "examples": [
            "typescript/5-4-closure-narrowing"
          ]
        },
        {
          "id": "noinfer",
          "heading": "NoInfer",
          "body": [
            "`NoInfer<T>` marks a parameter that should be checked against `T` without helping to infer it, so a default value cannot widen the type the other arguments set. It works in JSDoc like any utility type."
          ],
          "examples": [
            "types/other-noinfer"
          ]
        },
        {
          "id": "group-by",
          "heading": "Object.groupBy and Map.groupBy",
          "body": [
            "The types for the two grouping functions arrived in 5.4 under `target: esnext`, and moved to the `es2024` lib in [TypeScript 5.7](/typescript/5-7/). The groups of `Object.groupBy` are optional properties, so under `strictNullChecks` each one has to be checked before use."
          ]
        },
        {
          "id": "preserve",
          "heading": "module preserve",
          "body": [
            "`\"module\": \"preserve\"` describes a bundler that keeps `import` and `require()` as written and resolves each through the matching `exports` condition. It implies `moduleResolution: bundler`, `esModuleInterop` and `resolveJsonModule`."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "A variable assigned inside a nested function is still not narrowed in callbacks, since that function might run at any time."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "5.4 was the last release to accept the options deprecated in 5.0, with `\"ignoreDeprecations\": \"5.0\"`; [5.5](/typescript/5-5/) stops honoring them. The other breaking changes are mostly about conditional types, which JSDoc rarely uses, and `enum`, which a `.js` file cannot declare."
          ]
        }
      ],
      "gotchas": [
        "`Object.groupBy` returns an object with a `null` prototype, which TypeScript's type does not show; `\"toString\" in groups` is false at run time."
      ],
      "related": [
        "types/union",
        "types/other-utility-types",
        "config/target",
        "config/module",
        "typescript/5-3",
        "typescript/5-5"
      ],
      "sources": [
        {
          "title": "TypeScript 5.4 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.4",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-4/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-3",
      "slug": "5-3",
      "title": "TypeScript 5.3 for JavaScript users",
      "version": "5.3",
      "group": "TypeScript 5",
      "released": "2023-11-20",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html",
      "checked": "examples",
      "summary": "TypeScript 5.3 for checked JavaScript: import attributes, switch (true) narrowing, instanceof with Symbol.hasInstance, and super checks on fields.",
      "intro": [
        "TypeScript 5.3 was announced on 20 November 2023. It supports import attributes, `with { type: \"json\" }`, which replaced the older `assert` syntax, and narrows in more places JavaScript code reaches for: `switch (true)`, comparisons with `true` and `false`, and `instanceof` against a class with a `Symbol.hasInstance` method.",
        "One optimization in the release reads as if tsc stopped parsing JSDoc, which would matter a great deal for checked JavaScript. It did not stop for `.js` files; see below."
      ],
      "examples": [],
      "changes": [
        {
          "id": "import-attributes",
          "heading": "Import attributes",
          "body": [
            "`import data from \"./data.json\" with { type: \"json\" }` tells the runtime what kind of module to expect. TypeScript does not check the attribute's value in 5.3 (5.4 began checking it against a global `ImportAttributes` type). Under `nodenext`, [5.7](/typescript/5-7/) made the attribute required for JSON."
          ],
          "examples": [
            "config/resolve-json-module-nodenext"
          ]
        },
        {
          "id": "switch-true",
          "heading": "switch (true) narrows",
          "body": [
            "Each `case` condition in a `switch (true)` now narrows inside that case, the way an `if` chain does."
          ],
          "examples": [
            "typescript/5-3-switch-true"
          ]
        },
        {
          "id": "has-instance",
          "heading": "instanceof and Symbol.hasInstance",
          "body": [
            "A class can decide what `instanceof` means with a static `[Symbol.hasInstance]` method. When that method is a type predicate, `value instanceof Point` narrows `value` to the predicate's type, which can be a plain object type rather than the class."
          ],
          "examples": [
            "typescript/5-3-has-instance"
          ]
        },
        {
          "id": "super-fields",
          "heading": "super cannot reach class fields",
          "body": [
            "`super.greet()` only finds methods on the base class's prototype. If `greet` is a class field (an arrow function assigned in the class body), it is not there and the call throws. TypeScript 5.3 reports it (TS2855)."
          ],
          "examples": [
            "typescript/5-3-super-field"
          ]
        },
        {
          "id": "jsdoc-parsing",
          "heading": "tsc skips JSDoc only in .ts files",
          "body": [
            "The release notes say tsc now avoids parsing JSDoc to save time. In TypeScript's source the default mode for tsc parses every JSDoc comment in JavaScript files, where JSDoc carries the types, and in `.ts` files only the comments that contain `@see` or `@link`. This site's tests check that behavior with TypeScript 5.9."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`resolution-mode` became stable for import types, so `import(\"pkg\", { with: { \"resolution-mode\": \"require\" } })` can pick a package's CommonJS or ES module types. It is a TypeScript type syntax and the notes show it only for `.ts` files; this site has not tested it inside JSDoc."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "The breaking changes are the DOM types and the new `super` check (TS2855)."
          ]
        }
      ],
      "gotchas": [
        "`switch (true)` narrowing is per case; once a case falls through into the next, the variable has the union of both narrowings."
      ],
      "related": [
        "config/resolve-json-module",
        "types/type-predicates",
        "types/union",
        "typescript/5-2",
        "typescript/5-4"
      ],
      "sources": [
        {
          "title": "TypeScript 5.3 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-3.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.3",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-3/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-2",
      "slug": "5-2",
      "title": "TypeScript 5.2 for JavaScript users",
      "version": "5.2",
      "group": "TypeScript 5",
      "released": "2023-08-24",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html",
      "checked": "examples",
      "summary": "TypeScript 5.2 for checked JavaScript: using declarations, array methods on unions of arrays, .ts paths in import types, and matching module settings.",
      "intro": [
        "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."
      ],
      "examples": [],
      "changes": [
        {
          "id": "using",
          "heading": "using declarations",
          "body": [
            "`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."
          ],
          "examples": [
            "typescript/5-2-using"
          ]
        },
        {
          "id": "array-unions",
          "heading": "Methods on a union of arrays",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/5-2-union-array-methods"
          ]
        },
        {
          "id": "ts-paths",
          "heading": ".ts paths in import types",
          "body": [
            "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`."
          ],
          "examples": [
            "typescript/5-2-import-ts-extension"
          ]
        },
        {
          "id": "module-match",
          "heading": "module and moduleResolution must match",
          "body": [
            "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."
          ]
        },
        {
          "id": "tuple-labels",
          "heading": "Labeled and unlabeled tuple elements together",
          "body": [
            "A tuple can label some elements and not others, `[first: string, ...string[]]`, which was an error before. It works inside JSDoc braces."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "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."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes:"
          ],
          "list": [
            "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."
      ],
      "related": [
        "types/import-types",
        "types/tuples",
        "config/module-resolution",
        "config/lib",
        "typescript/5-1",
        "typescript/5-3"
      ],
      "sources": [
        {
          "title": "TypeScript 5.2 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.2",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-1",
      "slug": "5-1",
      "title": "TypeScript 5.1 for JavaScript users",
      "version": "5.1",
      "group": "TypeScript 5",
      "released": "2023-06-01",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html",
      "checked": "examples",
      "summary": "TypeScript 5.1 for checked JavaScript: @returns {undefined} needs no return, unrelated getter and setter types, and @param snippets.",
      "intro": [
        "TypeScript 5.1 was announced on 1 June 2023. In JavaScript terms it relaxed two rules: a function whose JSDoc says it returns `undefined` no longer needs a `return` statement, and a getter and setter can have unrelated types when both are annotated.",
        "Editors gained snippet completions for `@param` tags in `.js` and `.ts` files, JSDoc-heavy files parse faster, and an explicit `typeRoots` no longer searches parent folders."
      ],
      "examples": [],
      "changes": [
        {
          "id": "undefined-returns",
          "heading": "Returning undefined without a return statement",
          "body": [
            "Before 5.1, only functions returning `void` or `any` could end without `return`. Now a function with `@returns {undefined}`, or one passed where a function returning `undefined` is expected, can too."
          ],
          "examples": [
            "typescript/5-1-returns-undefined"
          ]
        },
        {
          "id": "accessors",
          "heading": "Unrelated getter and setter types",
          "body": [
            "TypeScript 4.3 allowed a setter to accept more than its getter returns, but the getter's type still had to fit the setter. 5.1 drops that rule when both accessors have explicit types: in JavaScript, `@returns` on the getter and `@param` on the setter."
          ],
          "examples": [
            "typescript/5-1-unrelated-accessors"
          ]
        },
        {
          "id": "param-snippets",
          "heading": "Snippets for @param",
          "body": [
            "Typing `@param` in a JSDoc comment offers a completion that fills in the tag for each parameter, in `.js` files as well as `.ts`. It is an editor feature; nothing changes for tsc."
          ]
        },
        {
          "id": "type-roots",
          "heading": "typeRoots in module resolution",
          "body": [
            "When an import cannot be resolved the usual way, TypeScript now also looks in the `typeRoots` folders. And, as a breaking change, an explicit `typeRoots` no longer falls back to walking up parent folders for `node_modules/@types`: list every folder you need, including a monorepo root's."
          ]
        },
        {
          "id": "jsdoc-speed",
          "heading": "Faster JSDoc parsing",
          "body": [
            "The scanner hands the parser larger pieces of JSDoc comment text, a change the release notes say roughly halves parse time for very large, comment-heavy JavaScript files."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "An unannotated function with no `return` is still inferred to return `void`."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes:"
          ],
          "list": [
            "TypeScript itself needs Node.js 14.17 or later.",
            "A config with `typeRoots` may report TS2688 for types it used to find in a parent folder; add that folder to `typeRoots`."
          ]
        }
      ],
      "gotchas": [
        "The relaxed accessor rule needs both types written out; with an inferred getter, reading the property right after assigning through the setter can show the assigned type instead."
      ],
      "related": [
        "tags/returns",
        "types/void-and-never",
        "config/types",
        "errors/ts2355",
        "typescript/5-0",
        "typescript/5-2"
      ],
      "sources": [
        {
          "title": "TypeScript 5.1 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-1.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.1",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-1/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "5-0",
      "slug": "5-0",
      "title": "TypeScript 5.0 for JavaScript users",
      "version": "5.0",
      "group": "TypeScript 5",
      "released": "2023-03-16",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html",
      "checked": "examples",
      "summary": "TypeScript 5.0 for checked JavaScript: the @satisfies and @overload tags, @template const, extends with several configs, and moduleResolution bundler.",
      "intro": [
        "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](/typescript/5-5/) then stopped honoring."
      ],
      "examples": [],
      "changes": [
        {
          "id": "satisfies",
          "heading": "@satisfies",
          "body": [
            "`/** @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."
          ],
          "examples": [
            "tags/satisfies-typo"
          ]
        },
        {
          "id": "overload",
          "heading": "@overload",
          "body": [
            "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."
          ],
          "examples": [
            "tags/overload-find"
          ]
        },
        {
          "id": "const-template",
          "heading": "const type parameters",
          "body": [
            "`@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."
          ],
          "examples": [
            "types/inference-const-template"
          ]
        },
        {
          "id": "extends-array",
          "heading": "extends with several configs",
          "body": [
            "`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."
          ],
          "examples": [
            "typescript/5-0-extends-array"
          ]
        },
        {
          "id": "bundler",
          "heading": "moduleResolution bundler and the resolution options",
          "body": [
            "`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`."
          ],
          "examples": [
            "config/module-resolution-bundler"
          ]
        },
        {
          "id": "relational",
          "heading": "Comparing number | string with a number",
          "body": [
            "`>`, `<`, `>=` 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`."
          ],
          "examples": [
            "typescript/5-0-relational-coercion"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "How TypeScript reads the JSDoc tags it already knew is unchanged; the new tags are additions."
          ],
          "list": [
            "`verbatimModuleSyntax` keeps imports and exports as written; in a `.js` file it mainly reports ES module syntax in a CommonJS file (TS1287)."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes and deprecations:"
          ],
          "list": [
            "`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} */ ({ ... })`."
      ],
      "related": [
        "tags/satisfies",
        "tags/overload",
        "types/generic-inference",
        "config/extends",
        "config/module-resolution",
        "typescript/4-9"
      ],
      "sources": [
        {
          "title": "TypeScript 5.0 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 5.0",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-9",
      "slug": "4-9",
      "title": "TypeScript 4.9 for JavaScript users",
      "version": "4.9",
      "group": "TypeScript 4",
      "released": "2022-11-15",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html",
      "checked": "examples",
      "summary": "TypeScript 4.9 for checked JavaScript: the in operator narrows unknown data, equality checks against not-a-number are errors, and emit keeps every import.",
      "intro": [
        "TypeScript 4.9 was announced on 15 November 2022. Its headline feature, the `satisfies` operator, is TypeScript syntax; JavaScript files got the same check as the `@satisfies` tag one release later, in 5.0.",
        "What a `.js` file does get: the `in` operator narrows an object to one that has the property, which makes unknown JSON easier to check; an equality comparison with `NaN` is now an error; and when TypeScript writes JavaScript output from `.js` files, it keeps every import."
      ],
      "examples": [],
      "changes": [
        {
          "id": "in-narrowing",
          "heading": "The in operator narrows to an object with that property",
          "body": [
            "Before 4.9, `\"name\" in data` only narrowed between types that already listed `name`. Now it also narrows a type that does not mention the property: after the check, `data` is known to have a `name` of type `unknown`, which you can then test with `typeof`. That is the usual way to check the result of `JSON.parse`, which TypeScript types as `any`; give it an `unknown` type first and the checks below are required."
          ],
          "examples": [
            "typescript/4-9-in-narrowing"
          ]
        },
        {
          "id": "nan",
          "heading": "Equality checks against not-a-number",
          "body": [
            "Nothing is equal to `NaN`, not even `NaN`, so `value === NaN` is always false. TypeScript 4.9 reports such comparisons (TS2845) and suggests `Number.isNaN(value)`."
          ],
          "examples": [
            "typescript/4-9-nan-check"
          ]
        },
        {
          "id": "emit-imports",
          "heading": "JavaScript output keeps every import",
          "body": [
            "When TypeScript writes `.js` output from `.js` input (`allowJs` with an `outDir`), older versions dropped an import that was only used in JSDoc, such as a class named in a `@type` tag. 4.9 keeps imports in JavaScript files exactly as written. A project that only checks its files (`noEmit`, the `jsconfig.json` default) is not affected."
          ]
        },
        {
          "id": "satisfies-operator",
          "heading": "satisfies is TypeScript syntax",
          "body": [
            "The new operator checks a value against a type without changing the value's own type. Written in a `.js` file, it is a syntax error; the JSDoc form, `/** @satisfies {Type} */`, arrived in [TypeScript 5.0](/typescript/5-0/) and is covered on the [@satisfies page](/tags/satisfies/)."
          ],
          "examples": [
            "types/satisfies-operator-js"
          ]
        },
        {
          "id": "watch",
          "heading": "Watch mode uses file system events",
          "body": [
            "`tsc --watch` now waits for the operating system to report a change instead of polling files, and falls back to polling only where events are not available. The release notes describe the `watchOptions` settings for the cases where events misbehave."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "JSDoc tags and the way TypeScript reads them are the same as in 4.8. A project that only checks its files sees no difference from the emit change."
          ],
          "list": [
            "The `satisfies` keyword cannot be written in a `.js` file; use the tag from 5.0.",
            "The 4.8 rule still holds: a type-only name cannot be imported by name in a `.js` file."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the release notes (the check for `NaN` is listed as a new feature, the others as breaking changes); the first two can produce new errors in checked JavaScript."
          ],
          "list": [
            "Comparisons with `NaN` are errors (TS2845).",
            "`Promise.resolve` now unwraps nested promises with `Awaited`, so code that relied on getting `any` back may see a more precise type.",
            "If you write JavaScript output from `.js` files, check that imports used only in JSDoc are safe to keep at run time.",
            "Under `moduleResolution: node16`, a package's `exports` now wins over its `typesVersions`; this matters to library authors who publish types for several TypeScript versions."
          ]
        }
      ],
      "gotchas": [
        "`Number.isNaN(x)` and `x !== x` are the two checks that work; the global `isNaN()` converts its argument first, so `isNaN(\"abc\")` is `true`."
      ],
      "related": [
        "typescript/4-8",
        "typescript/5-0",
        "tags/satisfies",
        "types/any-and-unknown",
        "guides/javascript-runtime-types-vs-annotations"
      ],
      "sources": [
        {
          "title": "TypeScript 4.9 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-9.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.9",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-9/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-8",
      "slug": "4-8",
      "title": "TypeScript 4.8 for JavaScript users",
      "version": "4.8",
      "group": "TypeScript 4",
      "released": "2022-08-25",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-8.html",
      "checked": "examples",
      "summary": "TypeScript 4.8 for checked JavaScript: a @typedef can no longer be imported by name, comparing with [] is an error, and unknown narrows better.",
      "intro": [
        "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`."
      ],
      "examples": [],
      "changes": [
        {
          "id": "type-imports",
          "heading": "Types cannot be imported or exported by name in .js files",
          "body": [
            "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](/errors/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](/typescript/5-5/), the [@import tag](/tags/import/). To re-export a type, declare another `@typedef` for it; a `@typedef` is exported from its module already."
          ],
          "examples": [
            "typescript/4-8-import-typedef",
            "typescript/4-8-import-type-fix"
          ]
        },
        {
          "id": "literal-comparison",
          "heading": "Comparing with an object or array literal",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/4-8-compare-literal"
          ]
        },
        {
          "id": "unknown-narrowing",
          "heading": "unknown and unconstrained type parameters",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/4-8-unconstrained-generic"
          ]
        },
        {
          "id": "performance",
          "heading": "Faster --watch, --build and --incremental",
          "body": [
            "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."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "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."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "Most new errors in checked JavaScript come from these three changes."
          ],
          "list": [
            "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."
      ],
      "related": [
        "typescript/4-9",
        "typescript/4-7",
        "tags/import",
        "tags/typedef",
        "types/import-types",
        "types/generic-constraints"
      ],
      "sources": [
        {
          "title": "TypeScript 4.8 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-8.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.8",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-8/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-7",
      "slug": "4-7",
      "title": "TypeScript 4.7 for JavaScript users",
      "version": "4.7",
      "group": "TypeScript 4",
      "released": "2022-05-24",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html",
      "checked": "examples",
      "summary": "TypeScript 4.7 for checked JavaScript: Node.js ES module support with module nodenext, .mjs and .cjs files, package exports, and moduleDetection.",
      "intro": [
        "TypeScript 4.7 was announced on 24 May 2022. It is the release that taught TypeScript how Node.js loads ES modules: `module` and `moduleResolution` set to `node16` or `nodenext` follow the `\"type\"` field of `package.json`, the `.mjs` and `.cjs` extensions, and the `exports` and `imports` maps of packages.",
        "For JavaScript projects that run directly in Node.js this is the setting that makes tsc agree with the runtime, and it is what this site's [Node.js quick start](/start/node-esm/) uses. The release also added `moduleDetection`, narrowing for `obj[key]` with a constant key, and left-to-right inference for untyped callbacks in object literals."
      ],
      "examples": [],
      "changes": [
        {
          "id": "node-esm",
          "heading": "ES modules in Node.js: node16 and nodenext",
          "body": [
            "With `\"module\": \"nodenext\"`, a `.js` file is an ES module when the nearest `package.json` says `\"type\": \"module\"` and CommonJS otherwise. In an ES module, relative imports need their file extension, as Node.js requires, and `require` is not defined; `import.meta` and top-level `await` are allowed. TypeScript applies the same rules to `.js` files as to `.ts` files."
          ],
          "examples": [
            "start/node-esm-extension"
          ]
        },
        {
          "id": "mjs-cjs",
          "heading": ".mjs and .cjs files",
          "body": [
            "A `.mjs` file is always an ES module and a `.cjs` file always CommonJS, whatever `package.json` says. An ES module can import a CommonJS file with a default import, and TypeScript checks the call through the types it reads from the CommonJS file's JSDoc. If `include` lists files by extension, such as `*.js`, add `*.mjs` and `*.cjs` to check those files too; a folder pattern such as `src` picks them up already."
          ],
          "examples": [
            "typescript/4-7-commonjs-from-esm"
          ]
        },
        {
          "id": "exports",
          "heading": "Package exports, imports and self-references",
          "body": [
            "TypeScript reads a package's `exports` map, including a `types` condition, which the release notes say should come first. Inside your own package, the `imports` field (specifiers starting with `#`) and imports of the package by its own name resolve as Node.js resolves them, so a JavaScript project can use Node.js's own aliases instead of `paths`."
          ],
          "examples": [
            "config/paths-imports"
          ]
        },
        {
          "id": "module-detection",
          "heading": "moduleDetection",
          "body": [
            "A new option decides which files are modules. The default, `auto`, also counts a file as a module when `\"type\": \"module\"` applies under `node16` or `nodenext`; `force` treats every file as a module, so two plain scripts no longer share one global scope. The [moduleDetection page](/config/module-detection/) shows both."
          ],
          "examples": [
            "config/module-detection-force"
          ]
        },
        {
          "id": "narrowing",
          "heading": "Narrowing obj[key] and inference in object literals",
          "body": [
            "Narrowing now works on an element access whose key is a literal type or a `unique symbol`, such as a `const key = \"label\"`. And untyped callbacks in an object literal passed to a generic function are inferred left to right, so a later callback sees the type an earlier one produced."
          ],
          "examples": [
            "typescript/4-7-element-access-narrowing",
            "typescript/4-7-object-inference"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "The older settings keep their behavior: with `module: commonjs` or `es2020` and the older resolution modes, file extensions and `package.json` `type` are not consulted."
          ],
          "list": [
            "`node16` and `nodenext` were added, not switched on: a `jsconfig.json` has to set them.",
            "JSDoc tags are read as before."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "Moving a JavaScript project to `nodenext` usually means:"
          ],
          "list": [
            "Adding `.js` to relative imports in ES module files.",
            "Setting `\"type\": \"module\"` (or using `.mjs`) for files that use `import` and `export`.",
            "Checking packages whose `exports` lack a `types` condition; their types may stop resolving.",
            "Separately from `nodenext`, two breaking changes: `readonly` tuples now have a read-only `length`, and template strings that may hold a symbol are errors."
          ]
        }
      ],
      "gotchas": [
        "Since TypeScript 5.2, `module` and `moduleResolution` must match when either is `node16` or `nodenext`; setting only `\"module\": \"nodenext\"` is enough."
      ],
      "related": [
        "config/module",
        "config/module-resolution",
        "config/module-detection",
        "start/node-esm",
        "errors/ts2835",
        "typescript/4-8"
      ],
      "sources": [
        {
          "title": "TypeScript 4.7 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.7",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/",
          "publisher": "TypeScript blog"
        },
        {
          "title": "Modules: Choosing compiler options",
          "url": "https://www.typescriptlang.org/docs/handbook/modules/guides/choosing-compiler-options.html",
          "publisher": "TypeScript handbook"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-6",
      "slug": "4-6",
      "title": "TypeScript 4.6 for JavaScript users",
      "version": "4.6",
      "group": "TypeScript 4",
      "released": "2022-02-28",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-6.html",
      "checked": "examples",
      "summary": "TypeScript 4.6 for JavaScript: syntax and binding errors in every .js file, even unchecked ones, plus narrowing for destructured unions.",
      "intro": [
        "TypeScript 4.6 was announced on 28 February 2022. It changed something for every JavaScript file an editor opens, checked or not: TypeScript now reports JavaScript syntax and binding errors, such as a `const` declared twice, in `.js` files without `checkJs` or `// @ts-check`.",
        "For checked files the release adds narrowing that plain JavaScript benefits from: destructured parts of a discriminated union stay linked, and parameters typed by a union of tuples narrow each other."
      ],
      "examples": [],
      "changes": [
        {
          "id": "binding-errors",
          "heading": "Syntax and binding errors in every JavaScript file",
          "body": [
            "Before 4.6, an unchecked `.js` file got almost no errors. Now TypeScript reports JavaScript's own syntax and binding errors there too: a block-scoped name declared twice (TS2451), a modifier where JavaScript does not allow one, and the like. They show up in editors that use TypeScript's language service and when tsc reads a JavaScript file. Type errors still need `checkJs`.",
            "The release notes give one way to silence them in a file: `// @ts-nocheck` at the top. This site's tests add a second with TypeScript 5.9: the errors appear only when `checkJs` is left out of the config. An unchecked file with a duplicate `const` gets TS2451 then, and nothing with `\"checkJs\": false` or with `// @ts-nocheck`."
          ]
        },
        {
          "id": "destructured-unions",
          "heading": "Destructured discriminated unions",
          "body": [
            "When `kind` and `payload` are destructured from a discriminated union into `const` bindings (or parameters that are never reassigned), checking `kind` now narrows `payload` too."
          ],
          "examples": [
            "typescript/4-6-destructured-union"
          ]
        },
        {
          "id": "dependent-parameters",
          "heading": "Parameters that depend on each other",
          "body": [
            "A function typed with a rest parameter that is a union of tuples, `(...args: [\"count\", number] | [\"label\", string]) => string`, narrows its second parameter when you check the first. In JavaScript, put the function type in a `@type` tag on the variable that holds the function."
          ],
          "examples": [
            "typescript/4-6-dependent-parameters"
          ]
        },
        {
          "id": "jsdoc-names",
          "heading": "Mismatched @param names in TypeScript files",
          "body": [
            "In a checked `.js` file a `@param` name that matches no parameter was already an error (TS8024). 4.6 adds an editor suggestion for the same mistake in `.ts` files, where JSDoc carries no types."
          ],
          "examples": [
            "tags/param-misnamed"
          ]
        },
        {
          "id": "es2022",
          "heading": "target es2022",
          "body": [
            "The `es2022` target and lib bring `Array.prototype.at()`, `Object.hasOwn` and the `cause` option of `Error`."
          ]
        },
        {
          "id": "before-super",
          "heading": "Code before super()",
          "body": [
            "A subclass with field initializers may now run code before `super()` in its constructor, as long as that code does not touch `this`; before 4.6 any code there was an error."
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "Type checking in `.js` files is still opt-in: without `checkJs` or `// @ts-check`, the new errors are JavaScript errors, not type errors."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "Two breaking changes in the release notes:"
          ],
          "list": [
            "Unchecked JavaScript files now show syntax and binding errors; add `// @ts-nocheck` to a file you do not want TypeScript to report anything in, or set `\"checkJs\": false` for the whole project.",
            "An object rest on a generic value now drops methods and other members a spread would not copy."
          ]
        }
      ],
      "gotchas": [
        "`// @ts-nocheck` turns off everything TypeScript reports in that file, the new JavaScript errors included, so use it on files you cannot change rather than to hide one error."
      ],
      "related": [
        "config/ts-nocheck",
        "config/check-js",
        "types/discriminated-unions",
        "errors/ts2451",
        "typescript/4-5",
        "typescript/4-7"
      ],
      "sources": [
        {
          "title": "TypeScript 4.6 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-6.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.6",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-6/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-5",
      "slug": "4-5",
      "title": "TypeScript 4.5 for JavaScript users",
      "version": "4.5",
      "group": "TypeScript 4",
      "released": "2021-11-17",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html",
      "checked": "examples",
      "summary": "TypeScript 4.5 for checked JavaScript: @type {const} and default @template arguments in JSDoc, the Awaited type, and #field in obj checks.",
      "intro": [
        "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."
      ],
      "examples": [],
      "changes": [
        {
          "id": "const-assertions",
          "heading": "const assertions in JSDoc",
          "body": [
            "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."
          ],
          "examples": [
            "types/const-object"
          ]
        },
        {
          "id": "template-defaults",
          "heading": "Default type arguments in @template",
          "body": [
            "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."
          ],
          "examples": [
            "tags/template-default"
          ]
        },
        {
          "id": "awaited",
          "heading": "The Awaited type",
          "body": [
            "`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."
          ],
          "examples": [
            "types/awaited-load"
          ]
        },
        {
          "id": "private-in",
          "heading": "#field in obj",
          "body": [
            "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."
          ],
          "examples": [
            "typescript/4-5-private-in"
          ]
        },
        {
          "id": "lib-packages",
          "heading": "Replacing the DOM types with a package",
          "body": [
            "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](/typescript/6-0/) turns it off by default."
          ],
          "snippets": [
            {
              "file": "package.json",
              "lang": "json",
              "code": "{\n  \"devDependencies\": {\n    \"@typescript/lib-dom\": \"npm:@types/web\"\n  }\n}"
            }
          ]
        },
        {
          "id": "import-assertions",
          "heading": "Import assertions",
          "body": [
            "`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](/typescript/5-3/) replaced it with import attributes (`with`), and [5.8](/typescript/5-8/) rejects `assert` under `nodenext`."
          ],
          "examples": [
            "typescript/5-8-import-assert"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`as const` itself is still TypeScript syntax and an error in a `.js` file (TS8016); the JSDoc cast is the way to write it."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "The breaking changes are small:"
          ],
          "list": [
            "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."
      ],
      "related": [
        "types/const-assertions",
        "tags/template",
        "types/generic-defaults",
        "types/awaited",
        "tags/private",
        "typescript/4-4"
      ],
      "sources": [
        {
          "title": "TypeScript 4.5 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-5.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.5",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-5/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-4",
      "slug": "4-4",
      "title": "TypeScript 4.4 for JavaScript users",
      "version": "4.4",
      "group": "TypeScript 4",
      "released": "2021-08-26",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html",
      "checked": "examples",
      "summary": "TypeScript 4.4 for JavaScript: caught errors are unknown under strict, spelling suggestions in unchecked .js files, and checks stored in a const narrow.",
      "intro": [
        "TypeScript 4.4 was announced on 26 August 2021. It added `useUnknownInCatchVariables` to the `strict` family, so in a checked `.js` file under `strict`, the variable in `catch (err)` is `unknown` and has to be narrowed before you read `err.message`.",
        "It also brought \"Did you mean…?\" spelling suggestions to JavaScript files that are not checked at all, and narrowing through conditions stored in a `const`."
      ],
      "examples": [],
      "changes": [
        {
          "id": "unknown-catch",
          "heading": "Caught errors are unknown under strict",
          "body": [
            "JavaScript can throw anything, so TypeScript 4.4 added `useUnknownInCatchVariables`, which makes every `catch` variable `unknown`, and `strict` turns it on. Reading `err.message` is then an error (TS18046) until you narrow `err`, usually with `err instanceof Error`. Turn `useUnknownInCatchVariables` off to go back to `any`."
          ],
          "examples": [
            "config/use-unknown-in-catch-variables-on",
            "tags/throws-narrow"
          ]
        },
        {
          "id": "spelling",
          "heading": "Spelling suggestions in unchecked JavaScript",
          "body": [
            "Editors that use TypeScript's language service now offer spelling suggestions in `.js` files that are not checked: a misspelled name or property that TypeScript can match to one it knows gets a \"Did you mean…?\" suggestion. They are suggestions, not errors, and tsc does not print them; full checking still needs `checkJs` or `// @ts-check`.",
            "The release notes do not say how to turn them off. This site's tests show it with TypeScript 5.9: the suggestions (TS2570 for a name, TS2568 for a property) appear when `checkJs` is left out of the config, and `\"checkJs\": false` turns them off, together with the JavaScript errors [4.6](/typescript/4-6/) added."
          ]
        },
        {
          "id": "aliased-conditions",
          "heading": "Checks stored in a const",
          "body": [
            "Before 4.4, `const isText = typeof input === \"string\"` followed by `if (isText)` did not narrow `input`. Now it does, as long as the condition is stored in a `const` and the value it checks is a `const`, a `readonly` property or a parameter that is never assigned."
          ],
          "examples": [
            "typescript/4-4-aliased-condition"
          ]
        },
        {
          "id": "promise-checks",
          "heading": "A promise used as a condition",
          "body": [
            "TypeScript 4.3 reported a promise stored in a variable and then tested in an `if`; 4.4 also reports a call tested directly, `if (isAdmin())`, which is almost always a missing `await` (TS2801)."
          ],
          "examples": [
            "typescript/4-4-promise-call"
          ]
        },
        {
          "id": "exact-optional",
          "heading": "exactOptionalPropertyTypes",
          "body": [
            "A new option, not part of `strict`, makes an optional property mean \"may be missing\" rather than \"may be `undefined`\". It needs `strictNullChecks`."
          ],
          "examples": [
            "config/exact-optional-property-types-on"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "Without `strict` (or with `useUnknownInCatchVariables` off), caught errors stay `any`."
          ],
          "list": [
            "Class `static` blocks, added to TypeScript in this release, are JavaScript syntax, so they work in `.js` files as written."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes; the first two come with `strict`:"
          ],
          "list": [
            "TS18046 (`err` is of type `unknown`) wherever a `catch` block reads a property of the error.",
            "TS2801 where a promise is tested without `await`.",
            "Abstract properties cannot have initializers (TypeScript syntax only)."
          ]
        }
      ],
      "gotchas": [
        "A JSDoc type on the catch variable can only be `any` or `unknown` (anything else is TS1196), so the fix is a check such as `err instanceof Error`, which also holds at run time."
      ],
      "related": [
        "config/use-unknown-in-catch-variables",
        "config/strict",
        "errors/ts18046",
        "tags/throws",
        "config/exact-optional-property-types",
        "typescript/4-3"
      ],
      "sources": [
        {
          "title": "TypeScript 4.4 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-4.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.4",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-4/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-3",
      "slug": "4-3",
      "title": "TypeScript 4.3 for JavaScript users",
      "version": "4.3",
      "group": "TypeScript 4",
      "released": "2021-05-26",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html",
      "checked": "examples",
      "summary": "TypeScript 4.3 for checked JavaScript: setters that accept more types than their getters return, noImplicitOverride, {@link} in editors, and promise checks.",
      "intro": [
        "TypeScript 4.3 was announced on 26 May 2021. Its release notes are written for `.ts` files, but several changes reach JavaScript: a setter may accept more types than its getter returns, `#private` methods and accessors are checked, editors resolve the names in `{@link}` tags, and a promise tested in an `if` without `await` is reported.",
        "It also added the `override` keyword and `noImplicitOverride`. In a `.js` file the keyword is the `@override` tag, which TypeScript 5.9 checks; the 4.3 notes do not mention the tag, so this site does not date it."
      ],
      "examples": [],
      "changes": [
        {
          "id": "write-types",
          "heading": "Setters with a wider type than their getters",
          "body": [
            "A `set size(value)` can now take `string | number | boolean` while `get size()` returns `number`, as long as the getter's type is assignable to the setter's. In JavaScript, type the setter's parameter with `@param` and the getter with `@returns`."
          ],
          "examples": [
            "typescript/4-3-write-types"
          ]
        },
        {
          "id": "override",
          "heading": "noImplicitOverride",
          "body": [
            "With `noImplicitOverride` on, a method that replaces one from the base class must say so. In a `.js` file that is the `@override` tag; without it, TypeScript 5.9 reports TS4119."
          ],
          "examples": [
            "tags/override-missing"
          ]
        },
        {
          "id": "link",
          "heading": "{@link} in editors",
          "body": [
            "Editors resolve the name inside a `{@link}` tag, so hovering it shows the target and go to definition jumps there. Nothing checks that the name exists; a broken link is not an error."
          ],
          "examples": [
            "tags/link-inline"
          ]
        },
        {
          "id": "promise-checks",
          "heading": "A promise used as a condition",
          "body": [
            "Under `strictNullChecks`, a promise in an `if` condition is always truthy, so it is reported (TS2801). 4.3 reports a promise held in a variable; [4.4](/typescript/4-4/) extends it to a call tested directly."
          ],
          "examples": [
            "typescript/4-3-promise-variable"
          ]
        },
        {
          "id": "private-methods",
          "heading": "#private methods and accessors",
          "body": [
            "Methods, getters and setters can have `#private` names as well as fields, and `static` members too. These are JavaScript features, checked like the rest: using one outside its class is an error."
          ],
          "examples": [
            "typescript/4-3-private-methods"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "`@public`, `@private` and `@protected` work as before; `#private` names are the run-time alternative."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes:"
          ],
          "list": [
            "Promises tested in conditions are errors under `strictNullChecks`.",
            "`useDefineForClassFields` defaults to `true` for `target: esnext` (and `es2022` once 4.6 added that target), which matches how JavaScript class fields work and only changes emitted code.",
            "The DOM types dropped APIs no browser implements."
          ]
        }
      ],
      "gotchas": [
        "Annotate the getter too. If the getter's type is inferred, TypeScript 5.9 treats the property as a `string` right after you assign a string through the setter, in `.ts` and `.js` files alike; `@returns {number}` on the getter keeps it a number."
      ],
      "related": [
        "tags/override",
        "config/no-implicit-override",
        "tags/link",
        "tags/private",
        "typescript/4-4",
        "typescript/4-2"
      ],
      "sources": [
        {
          "title": "TypeScript 4.3 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-3.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.3",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-3/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-2",
      "slug": "4-2",
      "title": "TypeScript 4.2 for JavaScript users",
      "version": "4.2",
      "group": "TypeScript 4",
      "released": "2021-02-23",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-2.html",
      "checked": "examples",
      "summary": "TypeScript 4.2 for JavaScript: type arguments in a .js file parse as comparisons, untyped yield results are implicit any, and --explainFiles.",
      "intro": [
        "TypeScript 4.2 was announced on 23 February 2021. One of its breaking changes is about JavaScript files only: `f<T>(100)` in a `.js` file is now parsed the way JavaScript parses it, as two comparisons, `(f < T) > 100`. Type arguments were never allowed in JavaScript; now they are not even read as such.",
        "The release also added `--explainFiles`, which says why each file is part of the program, and new checks that apply to `.js` files: an implicit `any` from a `yield` expression, and a function tested in a condition instead of called."
      ],
      "examples": [],
      "changes": [
        {
          "id": "type-arguments",
          "heading": "Type arguments in .js files are comparisons",
          "body": [
            "JavaScript has no syntax for explicit type arguments, and since 4.2 TypeScript does not parse `first<string>(x)` as a call with one: it is `first < string > x`, which then fails as a comparison (TS2365, TS2693). Let inference choose the type, or give the argument a type with a JSDoc cast."
          ],
          "examples": [
            "types/inference-no-type-arguments"
          ]
        },
        {
          "id": "yield",
          "heading": "Implicit any from yield",
          "body": [
            "Under `noImplicitAny`, keeping the result of a `yield` is now an error (TS7057) when nothing tells TypeScript its type. Give the generator a type with `@returns {Generator<Yielded, Returned, Received>}`."
          ],
          "examples": [
            "typescript/4-2-loose-yield"
          ]
        },
        {
          "id": "uncalled-functions",
          "heading": "Functions tested instead of called",
          "body": [
            "Under `strictNullChecks`, a function used as a condition inside `&&` or `||` is reported when it is always defined: `isEnabled && item.visible` is almost always a missing `()` (TS2774)."
          ],
          "examples": [
            "typescript/4-2-uncalled-function"
          ]
        },
        {
          "id": "explain-files",
          "heading": "--explainFiles",
          "body": [
            "Run `tsc -p jsconfig.json --explainFiles` to print every file in the program and the reason it is there: matched by `include`, imported by another file, or a lib or `@types` package. It is the quickest way to find out why a file outside your source folder is checked."
          ],
          "snippets": [
            {
              "lang": "sh",
              "code": "npx tsc -p jsconfig.json --explainFiles"
            }
          ]
        },
        {
          "id": "index-signatures",
          "heading": "noPropertyAccessFromIndexSignature",
          "body": [
            "A new option, outside `strict`, requires bracket access for keys that only an index signature declares, so a typo in a dotted property name cannot pass as a lookup."
          ],
          "examples": [
            "config/no-property-access-from-index-signature-on"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "TypeScript still does not accept type arguments in JavaScript; the change is in how the text is parsed, which matters most to tools that read JavaScript through TypeScript's parser."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes:"
          ],
          "list": [
            "New TS7057 errors for generators under `noImplicitAny`.",
            "Uncalled-function checks in `&&` and `||` under `strictNullChecks`.",
            "An import path ending in `.d.ts` is an error; import the module path instead.",
            "The `in` operator's right side must be an object."
          ]
        }
      ],
      "gotchas": [
        "An underscore prefix (`_first`) now marks a destructured name as intentionally unused for `noUnusedLocals`."
      ],
      "related": [
        "types/generic-inference",
        "config/no-implicit-any",
        "config/no-property-access-from-index-signature",
        "errors/ts2365",
        "typescript/4-1",
        "typescript/4-3"
      ],
      "sources": [
        {
          "title": "TypeScript 4.2 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-2.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.2",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-2/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-1",
      "slug": "4-1",
      "title": "TypeScript 4.1 for JavaScript users",
      "version": "4.1",
      "group": "TypeScript 4",
      "released": "2020-11-19",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html",
      "checked": "examples",
      "summary": "TypeScript 4.1 for JavaScript: checkJs implies allowJs, @see in editors, noUncheckedIndexedAccess, and resolve() needs a value.",
      "intro": [
        "TypeScript 4.1 was announced on 19 November 2020. For JavaScript projects its most visible change is a small one: `checkJs` now implies `allowJs`, so a `tsconfig.json` needs only one of them. (A `jsconfig.json` already defaulted `allowJs` to `true`.)",
        "It also added `noUncheckedIndexedAccess`, template literal types and key remapping, editor support for `@see`, and a breaking change that reaches most JavaScript code that creates promises: `resolve()` now needs a value unless the promise is a `Promise<void>`."
      ],
      "examples": [],
      "changes": [
        {
          "id": "check-js",
          "heading": "checkJs implies allowJs",
          "body": [
            "Setting `\"checkJs\": true` alone now brings `.js` files into the program and checks them. This site's tests check the rule against TypeScript 5.9; the [checkJs page](/config/check-js/) has the details."
          ],
          "examples": [
            "config/check-js-on"
          ]
        },
        {
          "id": "promise-resolve",
          "heading": "resolve() needs a value",
          "body": [
            "The `resolve` function of `new Promise` takes a value that is no longer optional, so `resolve()` with nothing is an error unless the promise is typed `Promise<void>`. In a `.js` file the message says so (TS2810: `new Promise()` needs a JSDoc hint). The simplest hint is `@returns {Promise<void>}` on the function that returns the promise."
          ],
          "examples": [
            "typescript/4-1-promise-resolve"
          ]
        },
        {
          "id": "unchecked-index",
          "heading": "noUncheckedIndexedAccess",
          "body": [
            "A new option, outside `strict`, makes a lookup by index or key include `undefined`, so `items[0]` has to be checked before use."
          ],
          "examples": [
            "config/no-unchecked-indexed-access-on"
          ]
        },
        {
          "id": "template-literals",
          "heading": "Template literal types and key remapping",
          "body": [
            "Type syntax gained template literal types (`` `on${Capitalize<E>}` ``) and an `as` clause in mapped types. Both work inside JSDoc braces exactly as in a `.ts` file."
          ],
          "examples": [
            "types/mapped-getters"
          ]
        },
        {
          "id": "falsy",
          "heading": "any and unknown in && expressions",
          "body": [
            "In `value && expr`, an `unknown` (or `any`) `value` now makes the whole expression `unknown` (or `any`), because the falsy value may be what comes out. A function typed `@returns {boolean}` that returns such an expression fails; `!!value && …` fixes it."
          ],
          "examples": [
            "typescript/4-1-falsy-unknown"
          ]
        },
        {
          "id": "paths-without-base-url",
          "heading": "paths without baseUrl",
          "body": [
            "`paths` no longer needs `baseUrl`: without it, each path is resolved from the folder of the config file. Older `jsconfig.json` files often set `baseUrl` only for this, and [TypeScript 6.0](/typescript/6-0/#deprecated) deprecates `baseUrl`."
          ],
          "examples": [
            "config/paths-on"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "A `jsconfig.json` checked the same files before and after: it already defaulted `allowJs` on."
          ],
          "list": [
            "`@see` was already read as documentation; 4.1 adds go to definition on the names it mentions."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "New errors in checked JavaScript usually come from:"
          ],
          "list": [
            "`resolve()` without a value in `new Promise` (TS2810 in `.js` files).",
            "`x && …` with an `unknown` or `any` `x` in boolean-returning functions.",
            "Conditional spreads that now produce optional properties."
          ]
        }
      ],
      "gotchas": [
        "Passing `resolve` itself, as in `setTimeout(resolve, ms)`, raises no error: the timer calls it with no arguments at run time, but TypeScript checks only direct calls such as `resolve()`."
      ],
      "related": [
        "config/check-js",
        "config/allow-js",
        "types/promise",
        "config/no-unchecked-indexed-access",
        "tags/see",
        "typescript/4-0"
      ],
      "sources": [
        {
          "title": "TypeScript 4.1 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-1.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.1",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-1/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    },
    {
      "id": "4-0",
      "slug": "4-0",
      "title": "TypeScript 4.0 for JavaScript users",
      "version": "4.0",
      "group": "TypeScript 4",
      "released": "2020-08-20",
      "notes": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html",
      "checked": "examples",
      "summary": "TypeScript 4.0 for checked JavaScript: @deprecated in editors, ??= and ||=, variadic tuples in JSDoc, and delete needs an optional property.",
      "intro": [
        "TypeScript 4.0 was announced on 20 August 2020. For JavaScript checked with JSDoc it brought `@deprecated` support in editors, checking for the new `&&=`, `||=` and `??=` operators, variadic and labeled tuple types that work inside JSDoc braces, and `--incremental` together with `noEmit`, the `jsconfig.json` default.",
        "Its breaking changes reach `.js` files too: under `strictNullChecks`, `delete` is only allowed on optional properties, and a class field can no longer replace a getter from its base class."
      ],
      "examples": [],
      "changes": [
        {
          "id": "deprecated",
          "heading": "@deprecated in editors",
          "body": [
            "TypeScript reads `/** @deprecated */` and reports uses of the name as a suggestion: editors strike the name through and show the reason in hovers and completions. It is never an error, and tsc prints nothing."
          ],
          "examples": [
            "tags/deprecated-function"
          ]
        },
        {
          "id": "logical-assignment",
          "heading": "&&=, ||= and ??=",
          "body": [
            "The three logical assignment operators are checked in `.js` files, and the checker follows them: after `options.retries ??= 3`, `options.retries` is a `number`, not `number | undefined`."
          ],
          "examples": [
            "typescript/4-0-logical-assignment"
          ]
        },
        {
          "id": "tuples",
          "heading": "Variadic and labeled tuple types",
          "body": [
            "A tuple type can spread a generic tuple, `[...T, ...U]`, so a function that joins two tuples can return their exact element types. Tuple elements can also be labeled, `[start: number, end: number]`, which editors show in hovers. Both work in JSDoc."
          ],
          "examples": [
            "typescript/4-0-variadic-tuples"
          ]
        },
        {
          "id": "incremental",
          "heading": "--incremental with noEmit",
          "body": [
            "A `jsconfig.json` sets `noEmit` by default, and until 4.0 that ruled out `incremental`. Now the two combine: tsc writes a `.tsbuildinfo` file and uses it to recheck only what changed. Checked here with TypeScript 5.9: with `\"incremental\": true`, `tsc -p jsconfig.json` writes `jsconfig.tsbuildinfo` beside the config, or the path in `tsBuildInfoFile`."
          ],
          "snippets": [
            {
              "file": "jsconfig.json",
              "lang": "jsonc",
              "code": "{\n  \"compilerOptions\": {\n    \"checkJs\": true,\n    \"strict\": true,\n    \"incremental\": true,\n    // keep the cache out of the source folder\n    \"tsBuildInfoFile\": \".cache/jsconfig.tsbuildinfo\"\n  },\n  \"include\": [\"src\"]\n}"
            }
          ]
        },
        {
          "id": "delete",
          "heading": "delete needs an optional property",
          "body": [
            "Under `strictNullChecks`, the operand of `delete` must be optional (or `any`, `unknown` or `never`), because deleting a required property leaves an object that no longer fits its type (TS2790)."
          ],
          "examples": [
            "typescript/4-0-delete-optional"
          ]
        },
        {
          "id": "accessors",
          "heading": "Fields and accessors cannot replace each other",
          "body": [
            "A class field that overrides a getter or setter from the base class, or the other way round, is now always an error (TS2610, TS2611); before 4.0 it was an error only with `useDefineForClassFields`. JavaScript classes are checked the same way."
          ],
          "examples": [
            "typescript/4-0-accessor-override"
          ]
        }
      ],
      "sections": [
        {
          "id": "unchanged",
          "heading": "What did not change",
          "body": [
            "Two 4.0 features were written for `.ts` files:"
          ],
          "list": [
            "`catch (e: unknown)` annotates a catch variable in a `.ts` file. In a `.js` file, TypeScript 5.9 reads the same annotation written as `catch (/** @type {unknown} */ e)` (the release notes do not date that form); [TypeScript 4.4](/typescript/4-4/) added `useUnknownInCatchVariables` to make every catch variable `unknown`.",
            "Class property inference from constructor assignments came to `.ts` files under `noImplicitAny`. A checked `.js` file already declares a class property from `this.name = …` in the constructor."
          ]
        },
        {
          "id": "upgrade",
          "heading": "Upgrade notes",
          "body": [
            "From the breaking changes:"
          ],
          "list": [
            "`delete` on a required property is an error under `strictNullChecks`.",
            "A field may not override an accessor, nor an accessor a field.",
            "The DOM types dropped `document.origin`; use `self.origin`."
          ]
        }
      ],
      "gotchas": [
        "`@deprecated` on one of several `@overload` signatures produced no suggestion in this site's tests, so mark the whole function."
      ],
      "related": [
        "tags/deprecated",
        "types/tuples",
        "config/strict-null-checks",
        "config/no-emit",
        "typescript/4-1"
      ],
      "sources": [
        {
          "title": "TypeScript 4.0 release notes",
          "url": "https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-0.html",
          "publisher": "TypeScript handbook"
        },
        {
          "title": "Announcing TypeScript 4.0",
          "url": "https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/",
          "publisher": "TypeScript blog"
        }
      ],
      "updated": "2026-10-20"
    }
  ]
}
