TypeScript 2.3

ES5/ES3 的生成器(Generators)與迭代(Iteration)

首先,瞭解一些 ES2016 術語

迭代器(Iterators)

ES2015 引入了 Iterator,它是一個公開了 nextreturnthrow 三個方法的物件,具體介面如下:

ts
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}

這種型別的迭代器對於遍歷同步獲取的值(例如陣列的元素或 Map 的鍵)非常有用。如果一個物件具有返回 Iterator 物件的 Symbol.iterator 方法,則稱該物件是“可迭代的”(iterable)。

迭代器協議還定義了某些 ES2015 特性的目標,例如 for..of、展開運算子(spread operator)以及解構賦值中的陣列剩餘部分(array rest)。

生成器(Generators)

ES2015 還引入了“生成器”(Generators),它們是可以透過 Iterator 介面和 yield 關鍵字用於產生部分計算結果的函式。生成器還可以透過 yield * 在內部將呼叫委託給另一個可迭代物件。例如:

ts
function* f() {
yield 1;
yield* [2, 3];
}
新的 --downlevelIteration

之前,生成器僅在目標環境為 ES6/ES2015 或更高版本時才受支援。此外,操作迭代器協議的結構(例如 for..of)在 ES6/ES2015 以下的目標版本中,僅在運算元組時才受支援。

TypeScript 2.3 透過 downlevelIteration 標誌,為 ES3 和 ES5 目標版本增加了對生成器和迭代器協議的全面支援。

使用 downlevelIteration 時,編譯器會使用新的型別檢查和編譯行為:如果找到 [Symbol.iterator]() 方法,它會嘗試在被迭代的物件上呼叫該方法;如果找不到,則會在該物件上建立一個合成的陣列迭代器。

請注意,對於任何非陣列值,這需要在執行時具備原生的 Symbol.iteratorSymbol.iterator 補丁(shim)。

當使用 downlevelIteration 時,for..of 語句、陣列解構以及陣列、呼叫和構造表示式中的展開元素,如果可用,都支援 Symbol.iterator。但即使在執行時或設計時未定義 Symbol.iterator,它們仍然可以用於陣列。

非同步迭代(Async Iteration)

TypeScript 2.3 增加了對非同步迭代器和生成器的支援,如當前的 TC39 提案所述。

非同步迭代器(Async iterators)

非同步迭代引入了 AsyncIterator,它類似於 Iterator。不同之處在於,AsyncIteratornextreturnthrow 方法返回的是迭代結果的 Promise,而不是結果本身。這允許呼叫者在 AsyncIterator 推進到產生值的時刻進行非同步通知註冊。AsyncIterator 具有以下形態:

ts
interface AsyncIterator<T> {
next(value?: any): Promise<IteratorResult<T>>;
return?(value?: any): Promise<IteratorResult<T>>;
throw?(e?: any): Promise<IteratorResult<T>>;
}

如果一個物件具有返回 AsyncIterator 物件的 Symbol.asyncIterator 方法,則稱該物件是“可非同步迭代的”(async-iterable)。

非同步生成器(Async Generators)

非同步迭代提案引入了“非同步生成器”,它們是同樣可以用於產生部分計算結果的非同步函式。非同步生成器還可以透過 yield* 將呼叫委託給可迭代物件或非同步可迭代物件。

ts
async function* g() {
yield 1;
await sleep(100);
yield* [2, 3];
yield* (async function*() {
await sleep(100);
yield 4;
})();
}

與生成器一樣,非同步生成器只能是函式宣告、函式表示式或類/物件字面量的方法。箭頭函式不能作為非同步生成器。除了有效的 Symbol.asyncIterator 引用(原生 Symbol 或補丁)外,非同步生成器還需要一個有效的全域性 Promise 實現(原生實現或與 ES2015 相容的 polyfill)。

for-await-of 語句

最後,ES2015 引入了 for..of 語句作為遍歷可迭代物件的一種方式。同樣,非同步迭代提案引入了 for..await..of 語句來遍歷非同步可迭代物件。

ts
async function f() {
for await (const x of g()) {
console.log(x);
}
}

for..await..of 語句僅在非同步函式或非同步生成器內合法。

注意事項
  • 請記住,我們對非同步迭代器的支援依賴於執行時對 Symbol.asyncIterator 的支援。您可能需要對 Symbol.asyncIterator 進行 polyfill,簡單的實現可以是:(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator");
  • 如果您還沒有 AsyncIterator 宣告,還需要在 lib 選項中包含 esnext
  • 最後,如果您的目標版本是 ES5 或 ES3,您還需要設定 --downlevelIterators 標誌。

泛型引數預設值

TypeScript 2.3 增加了對宣告泛型型別引數預設值的支援。

示例

考慮一個建立新 HTMLElement 的函式,無引數呼叫它時生成一個 Div;您還可以選擇性地傳遞子元素列表。以前您必須這樣定義它:

ts
declare function create(): Container<HTMLDivElement, HTMLDivElement[]>;
declare function create<T extends HTMLElement>(element: T): Container<T, T[]>;
declare function create<T extends HTMLElement, U extends HTMLElement>(
element: T,
children: U[]
): Container<T, U[]>;

使用泛型引數預設值,我們可以將其簡化為:

ts
declare function create<T extends HTMLElement = HTMLDivElement, U = T[]>(
element?: T,
children?: U
): Container<T, U>;

泛型引數預設值遵循以下規則:

  • 如果型別引數具有預設值,則視為可選。
  • 必選型別引數不能跟在可選型別引數後面。
  • 型別引數的預設型別必須滿足該型別引數的約束(如果存在)。
  • 指定型別實參時,只需為必選型別引數指定型別實參。未指定的型別引數將解析為它們的預設型別。
  • 如果指定了預設型別且推理無法選擇候選者,則推斷使用該預設型別。
  • 與現有類或介面宣告合併的類或介面宣告,可以為現有的型別引數引入預設值。
  • 與現有類或介面宣告合併的類或介面宣告,可以引入新的型別引數,只要它指定了預設值。

新的 --strict 總控選項

TypeScript 中新增的新檢查預設通常是關閉的,以避免破壞現有專案。雖然避免破壞是件好事,但這種策略的缺點是使得選擇最高級別的型別安全變得日益複雜,且每次 TypeScript 釋出時都需要採取顯式的選擇加入行動。透過 strict 選項,現在可以實現選擇最高級別的型別安全,前提是需要理解隨著改進的型別檢查特性的新增,編譯器可能會報告額外的錯誤。

新的 strict 編譯器選項代表了多項型別檢查選項的推薦設定。具體來說,指定 strict 相當於指定了以下所有選項(將來可能會包含更多選項):

精確地說,strict 選項為上述列出的編譯器選項設定了預設值。這意味著仍然可以單獨控制這些選項。例如:

sh
--strict --noImplicitThis false

這將起到開啟所有嚴格選項除了 noImplicitThis 選項的效果。使用此方案,可以表達由所有嚴格選項組成的配置,但排除某些明確列出的選項。換句話說,現在可以預設採用最高級別的型別安全,但可以選擇關閉某些檢查。

從 TypeScript 2.3 開始,由 tsc --init 生成的預設 tsconfig.json"compilerOptions" 部分包含一個 "strict": true 設定。因此,使用 tsc --init 啟動的新專案將預設啟用最高級別的型別安全。

增強的 --init 輸出

除了預設開啟 strict 外,tsc --init 還改進了輸出。由 tsc --init 生成的預設 tsconfig.json 檔案現在包含了一組常用編譯器選項及其描述,並已註釋掉。只需取消註釋您想要設定的配置即可獲得預期的行為;我們希望新的輸出能夠簡化新專案的設定,並在專案增長時保持配置檔案清晰可讀。

使用 --checkJs 檢查 .js 檔案中的錯誤

預設情況下,TypeScript 編譯器不會報告 .js 檔案中的任何錯誤,即使使用了 allowJs。透過 TypeScript 2.3,可以使用 checkJs.js 檔案中報告型別檢查錯誤。

您可以透過新增 // @ts-nocheck 註釋來跳過對某些檔案的檢查;相反,您可以選擇透過新增 // @ts-check 註釋來僅檢查少數幾個 .js 檔案,而無需設定 checkJs。您還可以透過在特定行前新增 // @ts-ignore 來忽略該行的錯誤。

.js 檔案仍會進行檢查以確保它們僅包含標準的 ECMAScript 特性;型別註解僅允許在 .ts 檔案中使用,在 .js 檔案中會被標記為錯誤。JSDoc 註釋可以用於向 JavaScript 程式碼新增一些型別資訊,有關支援的 JSDoc 結構的詳細資訊,請參閱 JSDoc 支援文件

有關更多詳細資訊,請參閱 型別檢查 JavaScript 檔案文件

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

此頁面的貢獻者
MHMohamed Hegazy (51)
OTOrta Therox (12)
NSNick Schonning (1)
JBJack Bates (1)
LRLars Reimann (1)
7+

最後更新:2026 年 3 月 27 日