Skip to content

Commit 471221d

Browse files
authored
Add 5.9 blog post (#3430)
1 parent 0750d25 commit 471221d

File tree

2 files changed

+255
-0
lines changed

2 files changed

+255
-0
lines changed
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
title: TypeScript 5.9
3+
layout: docs
4+
permalink: /docs/handbook/release-notes/typescript-5-9.html
5+
oneline: TypeScript 5.9 Release Notes
6+
---
7+
8+
9+
## Minimal and Updated `tsc --init`
10+
11+
For a while, the TypeScript compiler has supported an `--init` flag that can create a `tsconfig.json` within the current directory.
12+
In the last few years, running `tsc --init` created a very "full" `tsconfig.json`, filled with commented-out settings and their descriptions.
13+
We designed this with the intent of making options discoverable and easy to toggle.
14+
15+
However, given external feedback (and our own experience), we found it's common to immediately delete most of the contents of these new `tsconfig.json` files.
16+
When users want to discover new options, we find they rely on auto-complete from their editor, or navigate to [the tsconfig reference on our website](https://www.typescriptlang.org/tsconfig/) (which the generated `tsconfig.json` links to!).
17+
What each setting does is also documented on that same page, and can be seen via editor hovers/tooltips/quick info.
18+
While surfacing some commented-out settings might be helpful, the generated `tsconfig.json` was often considered overkill.
19+
20+
We also felt that it was time that `tsc --init` initialized with a few more prescriptive settings than we already enable.
21+
We looked at some common pain points and papercuts users have when they create a new TypeScript project.
22+
For example, most users write in modules (not global scripts), and `--moduleDetection` can force TypeScript to treat every implementation file as a module.
23+
Developers also often want to use the latest ECMAScript features directly in their runtime, so `--target` can typically be set to `esnext`.
24+
JSX users often find that going back to set `--jsx` is needless friction, and its options are slightly confusing.
25+
And often, projects end up loading more declaration files from `node_modules/@types` than TypeScript actually needs; but specifying an empty `types` array can help limit this.
26+
27+
In TypeScript 5.9, a plain `tsc --init` with no other flags will generate the following `tsconfig.json`:
28+
29+
```json5
30+
{
31+
// Visit https://aka.ms/tsconfig to read more about this file
32+
"compilerOptions": {
33+
// File Layout
34+
// "rootDir": "./src",
35+
// "outDir": "./dist",
36+
37+
// Environment Settings
38+
// See also https://aka.ms/tsconfig_modules
39+
"module": "nodenext",
40+
"target": "esnext",
41+
"types": [],
42+
// For nodejs:
43+
// "lib": ["esnext"],
44+
// "types": ["node"],
45+
// and npm install -D @types/node
46+
47+
// Other Outputs
48+
"sourceMap": true,
49+
"declaration": true,
50+
"declarationMap": true,
51+
52+
// Stricter Typechecking Options
53+
"noUncheckedIndexedAccess": true,
54+
"exactOptionalPropertyTypes": true,
55+
56+
// Style Options
57+
// "noImplicitReturns": true,
58+
// "noImplicitOverride": true,
59+
// "noUnusedLocals": true,
60+
// "noUnusedParameters": true,
61+
// "noFallthroughCasesInSwitch": true,
62+
// "noPropertyAccessFromIndexSignature": true,
63+
64+
// Recommended Options
65+
"strict": true,
66+
"jsx": "react-jsx",
67+
"verbatimModuleSyntax": true,
68+
"isolatedModules": true,
69+
"noUncheckedSideEffectImports": true,
70+
"moduleDetection": "force",
71+
"skipLibCheck": true,
72+
}
73+
}
74+
```
75+
76+
For more details, see the [implementing pull request](https://github.com/microsoft/TypeScript/pull/61813) and [discussion issue](https://github.com/microsoft/TypeScript/issues/58420).
77+
78+
## Support for `import defer`
79+
80+
TypeScript 5.9 introduces support for [ECMAScript's deferred module evaluation proposal](https://github.com/tc39/proposal-defer-import-eval/) using the new `import defer` syntax.
81+
This feature allows you to import a module without immediately executing the module and its dependencies, providing better control over when work and side-effects occur.
82+
83+
The syntax only permits namespace imports:
84+
85+
```ts
86+
import defer * as feature from "./some-feature.js";
87+
```
88+
89+
The key benefit of `import defer` is that the module is only evaluated when one of its exports is first accessed.
90+
Consider this example:
91+
92+
```ts
93+
// ./some-feature.ts
94+
initializationWithSideEffects();
95+
96+
function initializationWithSideEffects() {
97+
// ...
98+
specialConstant = 42;
99+
100+
console.log("Side effects have occurred!");
101+
}
102+
103+
export let specialConstant: number;
104+
```
105+
106+
When using `import defer`, the `initializationWithSideEffects()` function will not be called until you actually access a property of the imported namespace:
107+
108+
```ts
109+
import defer * as feature from "./some-feature.js";
110+
111+
// No side effects have occurred yet
112+
113+
// ...
114+
115+
// As soon as `specialConstant` is accessed, the contents of the `feature`
116+
// module are run and side effects have taken place.
117+
console.log(feature.specialConstant); // 42
118+
```
119+
120+
Because evaluation of the module is deferred until you access a member off of the module, you cannot use named imports or default imports with `import defer`:
121+
122+
```ts
123+
// ❌ Not allowed
124+
import defer { doSomething } from "some-module";
125+
126+
// ❌ Not allowed
127+
import defer defaultExport from "some-module";
128+
129+
// ✅ Only this syntax is supported
130+
import defer * as feature from "some-module";
131+
```
132+
133+
Note that when you write `import defer`, the module and its dependencies are fully loaded and ready for execution.
134+
That means that the module will need to exist, and will be loaded from the file system or a network resource.
135+
The key difference between a regular `import` and `import defer` is that *the execution of statements and declarations* is deferred until you access a property of the imported namespace.
136+
137+
This feature is particularly useful for conditionally loading modules with expensive or platform-specific initialization. It can also improve startup performance by deferring module evaluation for app features until they are actually needed.
138+
139+
Note that `import defer` is not transformed or "downleveled" at all by TypeScript.
140+
It is intended to be used in runtimes that support the feature natively, or by tools such as bundlers that can apply the appropriate transformation.
141+
That means that `import defer` will only work under the `--module` modes `preserve` and `esnext`.
142+
143+
We'd like to extend our thanks to [Nicolò Ribaudo](https://github.com/nicolo-ribaudo) who championed the proposal in TC39 and also provided [the implementation for this feature](https://github.com/microsoft/TypeScript/pull/60757).
144+
145+
## Support for `--module node20`
146+
147+
TypeScript provides several `node*` options for the `--module` and `--moduleResolution` settings.
148+
Most recently, `--module nodenext` has supported the ability to `require()` ECMAScript modules from CommonJS modules, and correctly rejects import assertions (in favor of the standards-bound [import attributes](https://github.com/tc39/proposal-import-attributes)).
149+
150+
TypeScript 5.9 brings a stable option for these settings called `node20`, intended to model the behavior of Node.js v20.
151+
This option is unlikely to have new behaviors in the future, unlike `--module nodenext` or `--moduleResolution nodenext`.
152+
Also unlike `nodenext`, specifying `--module node20` will imply `--target es2023` unless otherwise configured.
153+
`--module nodenext`, on the other hand, implies the floating `--target esnext`.
154+
155+
For more information, [take a look at the implementation here](https://github.com/microsoft/TypeScript/pull/61805).
156+
157+
## Summary Descriptions in DOM APIs
158+
159+
Previously, many of the DOM APIs in TypeScript only linked to the MDN documentation for the API.
160+
These links were useful, but they didn't provide a quick summary of what the API does.
161+
Thanks to a few changes from [Adam Naji](https://github.com/Bashamega), TypeScript now includes summary descriptions for many DOM APIs based on the MDN documentation.
162+
You can see more of these changes [here](https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1993) and [here](https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/1940).
163+
164+
## Expandable Hovers (Preview)
165+
166+
*Quick Info* (also called "editor tooltips" and "hovers") can be very useful for peeking at variables to see their types, or at type aliases to see what they actually refer to.
167+
Still, it's common for people to want to *go deeper* and get details from whatever's displayed within the quick info tooltip.
168+
For example, if we hover our mouse over the parameter `options` in the following example:
169+
170+
```ts
171+
export function drawButton(options: Options): void
172+
```
173+
174+
We're left with `(parameter) options: Options`.
175+
176+
![Tooltip for a parameter declared as `options` which just shows `options: Options`.](https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2025/06/bare-hover-5.8-01.png)
177+
178+
Do we really need to jump to the definition of the type `Options` just to see what members this value has?
179+
180+
Previously, that was actually the case.
181+
To help here, TypeScript 5.9 is now previewing a feature called *expandable hovers*, or "quick info verbosity".
182+
If you use an editor like VS Code, you'll now see a `+` and `-` button on the left of these hover tooltips.
183+
Clicking on the `+` button will expand out types more deeply, while clicking on the `-` button will collapse to the last view.
184+
185+
<video autoplay loop style="width: 100%;" src="https://devblogs.microsoft.com/typescript/wp-content/uploads/sites/11/2025/06/expandable-quick-info-1.mp4" aria-label="Expanding quick info to see more about the type of `Options`."></video>
186+
187+
This feature is currently in preview, and we are seeking feedback for both TypeScript and our partners on Visual Studio Code.
188+
For more details, see [the PR for this feature here](https://github.com/microsoft/TypeScript/pull/59940).
189+
190+
## Configurable Maximum Hover Length
191+
192+
Occasionally, quick info tooltips can become so long that TypeScript will truncate them to make them more readable.
193+
The downside here is that often the most important information will be omitted from the hover tooltip, which can be frustrating.
194+
To help with this, TypeScript 5.9's language server supports a configurable hover length, which can be configured in VS Code via the `js/ts.hover.maximumLength` setting.
195+
196+
Additionally, the new default hover length is substantially larger than the previous default.
197+
This means that in TypeScript 5.9, you should see more information in your hover tooltips by default.
198+
For more details, see [the PR for this feature here](https://github.com/microsoft/TypeScript/pull/61662) and [the corresponding change to Visual Studio Code here](https://github.com/microsoft/vscode/pull/248181).
199+
200+
## Optimizations
201+
202+
### Cache Instantiations on Mappers
203+
204+
When TypeScript replaces type parameters with specific type arguments, it can end up instantiating many of the same intermediate types over and over again.
205+
In complex libraries like Zod and tRPC, this could lead to both performance issues and errors reported around excessive type instantiation depth.
206+
Thanks to [a change](https://github.com/microsoft/TypeScript/pull/61505) from [Mateusz Burzyński](https://github.com/Andarist), TypeScript 5.9 is able to cache many intermediate instantiations when work has already begun on a specific type instantiation.
207+
This in turn avoids lots of unnecessary work and allocations.
208+
209+
### Avoiding Closure Creation in `fileOrDirectoryExistsUsingSource`
210+
211+
In JavaScript, a function expression will typically allocate a new function object, even if the wrapper function is just passing through arguments to another function with no captured variables.
212+
In code paths around file existence checks, [Vincent Bailly](https://github.com/VincentBailly) found examples of these pass-through function calls, even though the underlying functions only took single arguments.
213+
Given the number of existence checks that could take place in larger projects, he cited a speed-up of around 11%.
214+
[See more on this change here](https://github.com/microsoft/TypeScript/pull/61822/).
215+
216+
## Notable Behavioral Changes
217+
218+
### `lib.d.ts` Changes
219+
220+
Types generated for the DOM may have an impact on type-checking your codebase.
221+
222+
Additionally, one notable change is that `ArrayBuffer` has been changed in such a way that it is no longer a supertype of several different `TypedArray` types.
223+
This also includes subtypes of `UInt8Array`, such as `Buffer` from Node.js.
224+
As a result, you'll see new error messages such as:
225+
226+
```
227+
error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'BufferSource'.
228+
error TS2322: Type 'ArrayBufferLike' is not assignable to type 'ArrayBuffer'.
229+
error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
230+
error TS2322: Type 'Buffer' is not assignable to type 'ArrayBuffer'.
231+
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'string | Uint8Array<ArrayBufferLike>'.
232+
```
233+
234+
If you encounter issues with `Buffer`, you may first want to check that you are using the latest version of the `@types/node` package.
235+
This might include running
236+
237+
```
238+
npm update @types/node --save-dev
239+
```
240+
241+
Much of the time, the solution is to specify a more specific underlying buffer type instead of using the default `ArrayBufferLike` (i.e. explicitly writing out `Uint8Array<ArrayBuffer>` rather than a plain `Uint8Array`).
242+
In instances where some `TypedArray` (like `Uint8Array`) is passed to a function expecting an `ArrayBuffer` or `SharedArrayBuffer`, you can also try accessing the `buffer` property of that `TypedArray` like in the following example:
243+
244+
```diff
245+
let data = new Uint8Array([0, 1, 2, 3, 4]);
246+
- someFunc(data)
247+
+ someFunc(data.buffer)
248+
```
249+
250+
## Type Argument Inference Changes
251+
252+
In an effort to fix "leaks" of type variables during inference, TypeScript 5.9 may introduce changes in types and possibly new errors in some codebases.
253+
These are hard to predict, but can often be fixed by adding type arguments to generic functions calls.
254+
[See more details here](https://github.com/microsoft/TypeScript/pull/61668).

packages/documentation/scripts/types/AllFilenames.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ export type AllDocsPages =
131131
| "release-notes/TypeScript 5.6.md"
132132
| "release-notes/TypeScript 5.7.md"
133133
| "release-notes/TypeScript 5.8.md"
134+
| "release-notes/TypeScript 5.9.md"
134135
| "tutorials/ASP.NET Core.md"
135136
| "tutorials/Angular.md"
136137
| "tutorials/Babel with TypeScript.md"

0 commit comments

Comments
 (0)