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 日