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 日