TypeScript 5.9

精简并更新后的 tsc --init

长期以来,TypeScript 编译器一直支持 --init 标志,可以在当前目录中创建 tsconfig.json。在过去几年中,运行 tsc --init 会创建一个非常“完整”的 tsconfig.json,其中填充了大量的注释配置及其描述。我们这样设计是为了让用户更容易发现并切换这些选项。

然而,根据外部反馈(以及我们自身的经验),我们发现用户通常会立即删除这些新 tsconfig.json 文件中的大部分内容。当用户想要探索新选项时,他们更倾向于依赖编辑器的自动补全功能,或者访问我们网站上的 tsconfig 参考页面(生成的 tsconfig.json 中也有该页面的链接!)。每个设置的具体作用也记录在同一页面上,并且可以通过编辑器的悬停提示/工具提示/快速信息查看。虽然展示一些被注释掉的设置可能有所帮助,但生成的 tsconfig.json 通常被认为过于繁琐。

我们还认为,tsc --init 应该比我们之前提供的默认设置更加具有指导性。我们研究了用户在创建新的 TypeScript 项目时遇到的一些常见痛点和琐碎问题。例如,大多数用户使用模块进行编写(而不是全局脚本),而 --moduleDetection 可以强制 TypeScript 将每个实现文件视为模块。开发者通常也希望直接在运行时使用最新的 ECMAScript 特性,因此 --target 通常可以设置为 esnext。JSX 用户通常觉得回过头来设置 --jsx 是不必要的摩擦,且其选项有些令人困惑。此外,项目往往最终会加载比 TypeScript 实际需要更多的 node_modules/@types 声明文件;但指定一个空的 types 数组可以帮助限制这种情况。

在 TypeScript 5.9 中,不带任何其他标志的纯 tsc --init 将生成以下 tsconfig.json

json
{
// Visit https://aka.ms/tsconfig to read more about this file
"compilerOptions": {
// File Layout
// "rootDir": "./src",
// "outDir": "./dist",
// Environment Settings
// See also https://aka.ms/tsconfig_modules
"module": "nodenext",
"target": "esnext",
"types": [],
// For nodejs:
// "lib": ["esnext"],
// "types": ["node"],
// and npm install -D @types/node
// Other Outputs
"sourceMap": true,
"declaration": true,
"declarationMap": true,
// Stricter Typechecking Options
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
// Style Options
// "noImplicitReturns": true,
// "noImplicitOverride": true,
// "noUnusedLocals": true,
// "noUnusedParameters": true,
// "noFallthroughCasesInSwitch": true,
// "noPropertyAccessFromIndexSignature": true,
// Recommended Options
"strict": true,
"jsx": "react-jsx",
"verbatimModuleSyntax": true,
"isolatedModules": true,
"noUncheckedSideEffectImports": true,
"moduleDetection": "force",
"skipLibCheck": true,
}
}

更多详情,请参见实现该功能的 Pull Request讨论议题

支持 import defer

TypeScript 5.9 引入了对 ECMAScript 延迟模块评估提案的支持,并使用了新的 import defer 语法。此功能允许你导入模块而不立即执行该模块及其依赖项,从而更好地控制任务执行和副作用发生的时间。

该语法仅允许命名空间导入

ts
import defer * as feature from "./some-feature.js";

import defer 的主要好处是,只有在首次访问模块的导出成员时,该模块才会被评估。考虑这个例子

ts
// ./some-feature.ts
initializationWithSideEffects();
function initializationWithSideEffects() {
// ...
specialConstant = 42;
console.log("Side effects have occurred!");
}
export let specialConstant: number;

使用 import defer 时,initializationWithSideEffects() 函数只有在你真正访问所导入命名空间的属性时才会被调用

ts
import defer * as feature from "./some-feature.js";
// No side effects have occurred yet
// ...
// As soon as `specialConstant` is accessed, the contents of the `feature`
// module are run and side effects have taken place.
console.log(feature.specialConstant); // 42

由于模块的评估被推迟到你访问其成员时,因此你不能在 import defer 中使用命名导入或默认导入

ts
// ❌ Not allowed
import defer { doSomething } from "some-module";
// ❌ Not allowed
import defer defaultExport from "some-module";
// ✅ Only this syntax is supported
import defer * as feature from "some-module";

请注意,当你编写 import defer 时,模块及其依赖项已完全加载并准备好执行。这意味着该模块必须存在,并会从文件系统或网络资源中加载。常规 importimport defer 之间的关键区别在于,语句和声明的执行被推迟到你访问导入命名空间的属性时。

此功能对于有条件地加载具有昂贵或特定于平台初始化的模块特别有用。它还可以通过推迟应用功能的模块评估直到真正需要时,从而改善启动性能。

请注意,import defer 不会被 TypeScript 进行转换或“降级”。它旨在用于原生支持该特性的运行时,或由能够应用相应转换的工具(如打包器)处理。这意味着 import defer 仅在 --module 模式为 preserveesnext 时有效。

我们要感谢 Nicolò Ribaudo,他不仅在 TC39 中推进了该提案,还提供了此功能的实现

支持 --module node20

TypeScript 为 --module--moduleResolution 设置提供了多个 node* 选项。最近,--module nodenext 支持从 CommonJS 模块 require() ECMAScript 模块,并能正确拒绝导入断言(转而支持符合标准的导入属性)。

TypeScript 5.9 为这些设置带来了一个名为 node20 的稳定选项,旨在模拟 Node.js v20 的行为。与 --module nodenext--moduleResolution nodenext 不同,此选项将来不太可能出现新的行为。此外,与 nodenext 不同的是,除非另行配置,指定 --module node20 将隐含 --target es2023。而 --module nodenext 则隐含浮动的 --target esnext

有关更多信息,请查看此处的实现

DOM API 中的摘要说明

以前,TypeScript 中的许多 DOM API 只链接到该 API 的 MDN 文档。这些链接很有用,但它们没有提供关于 API 功能的快速摘要。得益于 Adam Naji 的几项改进,TypeScript 现在基于 MDN 文档包含了许多 DOM API 的摘要说明。你可以在此处此处查看更多这些更改。

可展开的悬停提示(预览)

快速信息(也称为“编辑器工具提示”或“悬停提示”)对于查看变量类型或查看类型别名实际指向的内容非常有用。尽管如此,人们经常希望深入了解悬停工具提示中显示的内容的详细信息。例如,如果我们鼠标悬停在以下示例中的参数 options

ts
export function drawButton(options: Options): void

我们看到的是 (parameter) options: Options

Tooltip for a parameter declared as options which just shows options: Options.

我们真的需要跳转到类型 Options 的定义才能看到该值有哪些成员吗?

以前确实需要这样。为了改善这一点,TypeScript 5.9 现已预览一项称为可展开悬停或“快速信息冗长程度”的功能。如果你使用像 VS Code 这样的编辑器,现在会在这些悬停工具提示的左侧看到一个 +- 按钮。点击 + 按钮将更深入地展开类型,而点击 - 按钮将折叠回之前的视图。

此功能目前处于预览阶段,我们正在征求 TypeScript 用户以及 Visual Studio Code 合作伙伴的反馈。更多详情,请参阅此处的功能 PR

可配置的最大悬停长度

有时,快速信息工具提示会变得非常长,以至于 TypeScript 会截断它们以提高可读性。其缺点是,通常最重要的信息会从悬停提示中被省略,这可能会令人沮丧。为了解决这个问题,TypeScript 5.9 的语言服务器支持可配置的悬停长度,可以在 VS Code 中通过 js/ts.hover.maximumLength 设置进行配置。

此外,新的默认悬停长度比之前的默认值要大得多。这意味着在 TypeScript 5.9 中,你应该默认在悬停提示中看到更多信息。更多详情,请参阅此处的功能 PR 以及 Visual Studio Code 中相应的更改

优化

在映射器上缓存实例化

当 TypeScript 将类型参数替换为具体的类型实参时,最终可能会反复实例化许多相同的中间类型。在 Zod 和 tRPC 等复杂库中,这可能会导致性能问题以及关于类型实例化深度过深的错误报告。得益于 Mateusz Burzyński更改,TypeScript 5.9 能够在针对特定类型实例化开始工作时缓存许多中间实例化。这反过来避免了大量不必要的工作和内存分配。

避免在 fileOrDirectoryExistsUsingSource 中创建闭包

在 JavaScript 中,函数表达式通常会分配一个新的函数对象,即使包装函数只是将参数传递给另一个没有捕获变量的函数。在文件存在性检查的代码路径中,Vincent Bailly 发现了这些传递函数的调用示例,尽管底层函数只接受单个参数。考虑到大型项目中可能进行的大量存在性检查,他指出这带来了大约 11% 的速度提升。在此处查看更多关于此更改的信息

显著的行为更改

lib.d.ts 变更

为 DOM 生成的类型可能会影响代码库的类型检查。

此外,一个显著的变化是 ArrayBuffer 已被修改,不再是几种不同 TypedArray 类型的超类型。这也包括 UInt8Array 的子类型,例如 Node.js 中的 Buffer。因此,你会看到类似这样的新错误消息:

error TS2345: Argument of type 'ArrayBufferLike' is not assignable to parameter of type 'BufferSource'.
error TS2322: Type 'ArrayBufferLike' is not assignable to type 'ArrayBuffer'.
error TS2322: Type 'Buffer' is not assignable to type 'Uint8Array<ArrayBufferLike>'.
error TS2322: Type 'Buffer' is not assignable to type 'ArrayBuffer'.
error TS2345: Argument of type 'Buffer' is not assignable to parameter of type 'string | Uint8Array<ArrayBufferLike>'.

如果你在使用 Buffer 时遇到问题,你可能首先要确认是否正在使用最新版本的 @types/node 包。这可能包括运行:

npm update @types/node --save-dev

通常,解决方案是指定一个更具体的底层缓冲区类型,而不是使用默认的 ArrayBufferLike(即显式写出 Uint8Array<ArrayBuffer> 而不是纯 Uint8Array)。在某些 TypedArray(如 Uint8Array)被传递给期望 ArrayBufferSharedArrayBuffer 的函数的情况下,你也可以尝试访问该 TypedArraybuffer 属性,如下例所示:

diff
let data = new Uint8Array([0, 1, 2, 3, 4]);
- someFunc(data)
+ someFunc(data.buffer)

类型实参推断更改

为了修复推断过程中类型变量的“泄露”,TypeScript 5.9 可能会在某些代码库中引入类型更改并可能导致新的错误。这些很难预测,但通常可以通过为泛型函数调用添加类型实参来修复。在此处查看更多详情

TypeScript 文档是一个开源项目。通过提交 Pull Request 帮助我们改进这些页面 ❤

此页面的贡献者
JBJake Bailey (6)

最后更新:2026 年 3 月 27 日