TypeScript 3.6

更嚴格的生成器(Stricter Generators)

TypeScript 3.6 為迭代器和生成器函式引入了更嚴格的檢查。在舊版本中,生成器的使用者無法區分一個值是從生成器中 yield(產出)的還是 return(返回)的。

ts
function* foo() {
if (Math.random() < 0.5) yield 100;
return "Finished!";
}
let iter = foo();
let curr = iter.next();
if (curr.done) {
// TypeScript 3.5 and prior thought this was a 'string | number'.
// It should know it's 'string' since 'done' was 'true'!
curr.value;
}

此外,生成器僅假定 yield 的型別始終為 any

ts
function* bar() {
let x: { hello(): void } = yield;
x.hello();
}
let iter = bar();
iter.next();
iter.next(123); // oops! runtime error!

在 TypeScript 3.6 中,檢查器現在知道在我們的第一個示例中 curr.value 的正確型別應該是 string,並且會對最後一個示例中的 next() 呼叫正確報錯。這得益於 IteratorIteratorResult 型別宣告的一些更改(引入了幾個新的型別引數),以及 TypeScript 用於表示生成器的一個名為 Generator 的新型別。

Iterator 型別現在允許使用者指定 yield 的型別、返回的型別以及 next 可以接受的型別。

ts
interface Iterator<T, TReturn = any, TNext = undefined> {
// Takes either 0 or 1 arguments - doesn't accept 'undefined'
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}

基於這些工作,新的 Generator 型別是一個同時具有 returnthrow 方法且可迭代的 Iterator

ts
interface Generator<T = unknown, TReturn = any, TNext = unknown>
extends Iterator<T, TReturn, TNext> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}

為了允許區分返回值和 yield 的值,TypeScript 3.6 將 IteratorResult 型別轉換為可辨識聯合型別(discriminated union type)。

ts
type IteratorResult<T, TReturn = any> =
| IteratorYieldResult<T>
| IteratorReturnResult<TReturn>;
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}

簡而言之,這意味著當直接處理來自迭代器的值時,你將能夠適當地收窄這些值。

為了正確表示透過呼叫 next() 可以傳遞給生成器的型別,TypeScript 3.6 還推斷了生成器函式體中 yield 的某些用法。

ts
function* foo() {
let x: string = yield;
console.log(x.toUpperCase());
}
let x = foo();
x.next(); // first call to 'next' is always ignored
x.next(42); // error! 'number' is not assignable to 'string'

如果你更喜歡顯式指定,也可以使用顯式返回型別來強制執行可以從 yield 表示式返回、產出和評估的值的型別。在下文中,next() 只能使用 boolean 值呼叫;根據 done 的值,value 可以是 stringnumber

ts
/**
* - yields numbers
* - returns strings
* - can be passed in booleans
*/
function* counter(): Generator<number, string, boolean> {
let i = 0;
while (true) {
if (yield i++) {
break;
}
}
return "done!";
}
var iter = counter();
var curr = iter.next();
while (!curr.done) {
console.log(curr.value);
curr = iter.next(curr.value === 5);
}
console.log(curr.value.toUpperCase());
// prints:
//
// 0
// 1
// 2
// 3
// 4
// 5
// DONE!

有關更改的更多詳細資訊,請參閱此處的 pull request

更準確的陣列展開(Array Spread)

在 ES2015 之前的目標版本中,對於 for/of 迴圈和陣列展開等結構的最高保真度編譯可能會顯得比較重。因此,TypeScript 預設使用一種僅支援陣列型別的簡化編譯方式,並使用 downlevelIteration 標誌來支援在其他型別上進行迭代。不帶 downlevelIteration 的寬鬆預設值效果尚可;但是,在某些常見情況下,陣列展開的轉換存在可觀察到的差異。例如,以下包含展開操作的陣列

ts
[...Array(5)];

可以重寫為以下陣列字面量

js
[undefined, undefined, undefined, undefined, undefined];

然而,TypeScript 會將原始程式碼轉換為如下程式碼

ts
Array(5).slice();

這略有不同。Array(5) 生成一個長度為 5 的陣列,但沒有任何定義的屬性槽位(property slots)。

TypeScript 3.6 引入了一個新的 __spreadArrays 輔助函式,以便在 downlevelIteration 之外的舊目標中準確模擬 ECMAScript 2015 中的行為。__spreadArrays 也可在 tslib 中使用。

有關更多資訊,請參閱相關的 pull request

改進 Promises 的使用者體驗

TypeScript 3.6 為 Promise 被錯誤處理的情況引入了一些改進。

例如,在將 Promise 內容傳遞給其他函式之前忘記 .then()await 它非常常見。TypeScript 的錯誤訊息現在是專門定製的,會告知使用者或許應該考慮使用 await 關鍵字。

ts
interface User {
name: string;
age: number;
location: string;
}
declare function getUserData(): Promise<User>;
declare function displayUser(user: User): void;
async function f() {
displayUser(getUserData());
// ~~~~~~~~~~~~~
// Argument of type 'Promise<User>' is not assignable to parameter of type 'User'.
// ...
// Did you forget to use 'await'?
}

await.then() 一個 Promise 之前嘗試訪問其方法也很常見。這是我們可以做得更好的眾多例子之一。

ts
async function getCuteAnimals() {
fetch("https://reddit.com/r/aww.json").json();
// ~~~~
// Property 'json' does not exist on type 'Promise<Response>'.
//
// Did you forget to use 'await'?
}

有關詳細資訊,請參閱原始 issue 以及連結回該 issue 的 pull requests。

更好的識別符號 Unicode 支援

當編譯到 ES2015 及更高版本的目標時,TypeScript 3.6 包含對識別符號中 Unicode 字元的更好支援。

ts
const 𝓱𝓮𝓵𝓵𝓸 = "world"; // previously disallowed, now allowed in '--target es2015'

SystemJS 中的 import.meta 支援

當你的 module 目標設定為 system 時,TypeScript 3.6 支援將 import.meta 轉換為 context.meta

ts
// This module:
console.log(import.meta.url);
// gets turned into the following:
System.register([], function (exports, context) {
return {
setters: [],
execute: function () {
console.log(context.meta.url);
},
};
});

Ambient 上下文中允許使用 getset 訪問器

在 TypeScript 的先前版本中,語言不允許在 Ambient 上下文(如 declare 類或通常在 .d.ts 檔案中)中使用 getset 訪問器。其理由是,就讀寫這些屬性而言,訪問器與屬性並沒有什麼區別;但是,由於 ECMAScript 的類欄位提案的行為可能與現有 TypeScript 版本中的行為不同,我們意識到我們需要一種方式來傳達這種差異,以便在子類中提供適當的錯誤提示。

因此,使用者可以在 TypeScript 3.6 的 Ambient 上下文中編寫 getter 和 setter。

ts
declare class Foo {
// Allowed in 3.6+.
get x(): number;
set x(val: number);
}

在 TypeScript 3.7 中,編譯器本身將利用此功能,以便生成的 .d.ts 檔案也能輸出 get/set 訪問器。

Ambient 類和函式可以合併

在舊版本的 TypeScript 中,在任何情況下合併類和函式都是錯誤的。現在,Ambient 類和函式(帶有 declare 修飾符的類/函式,或位於 .d.ts 檔案中)可以合併。這意味著現在你可以這樣寫

ts
export declare function Point2D(x: number, y: number): Point2D;
export declare class Point2D {
x: number;
y: number;
constructor(x: number, y: number);
}

而不是必須使用

ts
export interface Point2D {
x: number;
y: number;
}
export declare var Point2D: {
(x: number, y: number): Point2D;
new (x: number, y: number): Point2D;
};

這種做法的一個優點是,可以輕鬆表達可呼叫的建構函式模式,同時允許名稱空間與這些宣告合併(因為 var 宣告不能與 namespace 合併)。

在 TypeScript 3.7 中,編譯器將利用此功能,以便從 .js 檔案生成的 .d.ts 檔案可以適當地捕獲類函式的“可呼叫性”和“可構造性”。

有關更多詳細資訊,請參閱 GitHub 上的原始 PR

支援 --build--incremental 的 API

TypeScript 3.0 引入了對引用其他專案並透過 --build 標誌進行增量構建的支援。此外,TypeScript 3.4 引入了 incremental 標誌,用於儲存有關先前編譯的資訊,以便僅重新構建某些檔案。這些標誌對於更靈活地構建專案和加快構建速度非常有用。遺憾的是,使用這些標誌在 Gulp 和 Webpack 等第三方構建工具中無法正常工作。TypeScript 3.6 現在公開了兩組 API 來操作專案引用和增量程式構建。

為了建立 incremental 構建,使用者可以利用 createIncrementalProgramcreateIncrementalCompilerHost API。使用者還可以使用新公開的 readBuilderProgram 函式從該 API 生成的 .tsbuildinfo 檔案中重新恢復舊程式例項。該函式僅用於建立新程式(即,你無法修改返回的例項 - 它僅用於在其他 create*Program 函式中作為 oldProgram 引數)。

為了利用專案引用,公開了一個新的 createSolutionBuilder 函式,它返回一個新型別 SolutionBuilder 的例項。

有關這些 API 的更多詳細資訊,你可以檢視原始 pull request

感知分號的程式碼編輯

Visual Studio 和 Visual Studio Code 等編輯器可以自動應用快速修復、重構以及其他轉換(例如自動從其他模組匯入值)。這些轉換由 TypeScript 提供支援,而舊版本的 TypeScript 會無條件地在每個語句末尾新增分號;遺憾的是,這與許多使用者的風格指南不一致,許多使用者對編輯器自動插入分號感到不滿。

TypeScript 現在足夠智慧,可以在應用此類編輯時檢測你的檔案是否使用分號。如果你的檔案通常缺少分號,TypeScript 就不會新增分號。

有關更多詳細資訊,請參閱相應的 pull request

更智慧的自動匯入語法

JavaScript 有許多不同的模組語法或約定:ECMAScript 標準中的語法、Node 已經支援的語法 (CommonJS)、AMD、System.js 等等!在大多數情況下,TypeScript 預設使用 ECMAScript 模組語法進行自動匯入,這在某些具有不同編譯器設定的 TypeScript 專案中,或者在帶有純 JavaScript 和 require 呼叫的 Node 專案中通常是不合適的。

TypeScript 3.6 現在更智慧了,它會在決定如何自動匯入其他模組之前檢視你現有的匯入。你可以在 這裡檢視原始 pull request 中的更多詳細資訊

新的 TypeScript Playground

TypeScript Playground 迎來了亟需的更新,並具備了便捷的新功能!新的 Playground 在很大程度上是 Artem TyurinTypeScript Playground 的分支,社群成員一直在越來越多地使用它。我們非常感謝 Artem 在此提供的幫助!

新的 Playground 現在支援許多新選項,包括

  • target 選項(允許使用者從 es5 切換到 es3es2015esnext 等)
  • 所有的嚴格模式標誌(包括僅僅 strict
  • 支援純 JavaScript 檔案(使用 allowJS 和可選的 checkJs

這些選項在共享 Playground 示例連結時也會持久保留,從而使使用者能更可靠地共享示例,而不必告訴接收者“哦,別忘了開啟 noImplicitAny 選項!”。

在不久的將來,我們將重新整理 Playground 示例,增加 JSX 支援,並完善自動型別獲取,這意味著你將能夠在 Playground 上獲得與在個人編輯器中相同的體驗。

隨著我們改進 Playground 和網站,我們歡迎在 GitHub 上提出反饋和 pull requests

TypeScript 文件是一個開源專案。請傳送 Pull Request 來幫助我們改進這些頁面 ❤

此頁面的貢獻者
DRDaniel Rosenwasser (51)
OTOrta Therox (12)
ELEliran Levi (1)
JBJack Bates (1)
JRUJohn Ralph Umandal (1)
3+

最後更新:2026 年 3 月 27 日