TypeScript 2.3

ES5/ES3 的生成器和迭代

¥Generators and Iteration for ES5/ES3

首先介绍一些 ES2016 术语:

¥First some ES2016 terminology:

迭代器

¥Iterators

ES2015 引入了 Iterator 是一个对象,它根据以下接口公开了三个方法:nextreturnthrow

¥ES2015 introduced Iterator, which is an object that exposes three methods, next, return, and throw, as per the following interface:

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

这种迭代器对于迭代同步可用的值非常有用,例如数组的元素或 Map 的键。如果支持迭代的对象具有返回 Iterator 对象的 Symbol.iterator 方法,则称其为 “iterable”。

¥This kind of iterator is useful for iterating over synchronously available values, such as the elements of an Array or the keys of a Map. An object that supports iteration is said to be “iterable” if it has a Symbol.iterator method that returns an Iterator object.

Iterator 协议还定义了一些 ES2015 特性的目标,例如 for..of、展开运算符以及解构赋值中的数组剩余操作。

¥The Iterator protocol also defines the target of some of the ES2015 features like for..of and spread operator and the array rest in destructuring assignments.

生成器

¥Generators

ES2015 还引入了 “生成器”,这些函数可以通过 Iterator 接口和 yield 关键字用于生成部分计算结果。生成器还可以通过 yield * 在内部将调用委托给另一个可迭代对象。例如:

¥ES2015 also introduced “Generators”, which are functions that can be used to yield partial computation results via the Iterator interface and the yield keyword. Generators can also internally delegate calls to another iterable through yield *. For example:

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

新的 --downlevelIteration

¥New --downlevelIteration

之前,只有目标语言是 ES6/ES2015 或更高版本时才支持生成器。此外,操作迭代器协议的构造函数(例如 for..of)仅在 ES6/ES2015 以下的目标对数组进行操作时才受支持。

¥Previously generators were only supported if the target is ES6/ES2015 or later. Moreover, constructs that operate on the Iterator protocol, e.g. for..of were only supported if they operate on arrays for targets below ES6/ES2015.

TypeScript 2.3 添加了对生成器和迭代器协议的完整支持,并支持 ES3 和 ES5 目标平台的 downlevelIteration 标志。

¥TypeScript 2.3 adds full support for generators and the Iterator protocol for ES3 and ES5 targets with downlevelIteration flag.

使用 downlevelIteration 类型,编译器使用新的类型检查和触发行为,如果找到被迭代对象,则尝试调用该方法;如果找不到,则在该对象上创建一个合成数组迭代器。

¥With downlevelIteration, the compiler uses new type check and emit behavior that attempts to call a [Symbol.iterator]() method on the iterated object if it is found, and creates a synthetic array iterator over the object if it is not.

请注意,对于任何非数组值,这需要在运行时使用原生 Symbol.iteratorSymbol.iterator shim。

¥Please note that this requires a native Symbol.iterator or Symbol.iterator shim at runtime for any non-array values.

如果在使用 downlevelIteration 时 ES5/E3 中支持 Symbol.iteratorfor..of 语句、数组解构以及数组、Call 和 New 表达式中的 Spread 元素将支持 Symbol.iterator,但即使在运行时或设计时未定义 Symbol.iterator,也可以在数组上使用它们。

¥for..of statements, Array Destructuring, and Spread elements in Array, Call, and New expressions support Symbol.iterator in ES5/E3 if available when using downlevelIteration, but can be used on an Array even if it does not define Symbol.iterator at run time or design time.

异步迭代

¥Async Iteration

TypeScript 2.3 添加了对当前 TC39 提案 中描述的异步迭代器和生成器的支持。

¥TypeScript 2.3 adds support for the async iterators and generators as described by the current TC39 proposal.

异步迭代器

¥Async iterators

异步迭代引入了 AsyncIterator,它与 Iterator 类似。区别在于,AsyncIteratornextreturnthrow 方法返回的是 Promise 作为迭代结果,而不是结果本身。这使得调用者能够在 AsyncIterator 推进到产生值时加入异步通知。AsyncIterator 具有以下结构:

¥The Async Iteration introduces an AsyncIterator, which is similar to Iterator. The difference lies in the fact that the next, return, and throw methods of an AsyncIterator return a Promise for the iteration result, rather than the result itself. This allows the caller to enlist in an asynchronous notification for the time at which the AsyncIterator has advanced to the point of yielding a value. An AsyncIterator has the following shape:

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 方法,则称其为 “iterable”。

¥An object that supports async iteration is said to be “iterable” if it has a Symbol.asyncIterator method that returns an AsyncIterator object.

异步生成器

¥Async Generators

异步迭代提案 引入了 “异步生成器”,它们是异步函数,也可用于产生部分计算结果。异步生成器还可以通过 yield* 将调用委托给可迭代对象或异步可迭代对象:

¥The Async Iteration proposal introduces “Async Generators”, which are async functions that also can be used to yield partial computation results. Async Generators can also delegate calls via yield* to either an iterable or async iterable:

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

与生成器一样,异步生成器只能是函数声明、函数表达式、类方法或对象字面量。箭头函数不能是异步生成器。异步生成器需要一个有效的全局 Promise 实现(原生实现或兼容 ES2015 的 polyfill),以及一个有效的 Symbol.asyncIterator 引用(原生符号或填充程序)。

¥As with Generators, Async Generators can only be function declarations, function expressions, or methods of classes or object literals. Arrow functions cannot be Async Generators. Async Generators require a valid, global Promise implementation (either native or an ES2015-compatible polyfill), in addition to a valid Symbol.asyncIterator reference (either a native symbol or a shim).

for-await-of 语句

¥The for-await-of Statement

最后,ES2015 引入了 for..of 语句作为迭代可迭代对象的一种方式。同样,异步迭代提案引入了 for..await..of 语句来迭代异步可迭代对象:

¥Finally, ES2015 introduced the for..of statement as a means of iterating over an iterable. Similarly, the Async Iteration proposal introduces the for..await..of statement to iterate over an async iterable:

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

for..await..of 语句仅在异步函数或异步生成器中合法。

¥The for..await..of statement is only legal within an Async Function or Async Generator.

警告

¥Caveats

  • 请记住,我们对异步迭代器的支持依赖于运行时对 Symbol.asyncIterator 的支持。你可能需要对 Symbol.asyncIterator 进行 polyfill,为了简单起见,可以像这样简单:(Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator");

    ¥Keep in mind that our support for async iterators relies on support for Symbol.asyncIterator to exist at runtime. You may need to polyfill Symbol.asyncIterator, which for simple purposes can be as simple as: (Symbol as any).asyncIterator = Symbol.asyncIterator || Symbol.for("Symbol.asyncIterator");

  • 如果你还没有 AsyncIterator 声明,还需要在 lib 选项中包含 esnext 以获取它。

    ¥You also need to include esnext in your lib option, to get the AsyncIterator declaration if you do not already have it.

  • 最后,如果你的目标是 ES5 或 ES3,你还需要设置 --downlevelIterators 标志。

    ¥Finally, if your target is ES5 or ES3, you’ll also need to set the --downlevelIterators flag.

泛型参数默认值

¥Generic parameter defaults

TypeScript 2.3 添加了对泛型类型参数声明默认值的支持。

¥TypeScript 2.3 adds support for declaring defaults for generic type parameters.

示例

¥Example

考虑一个创建新 HTMLElement 的函数,调用该函数时不带参数会生成 Div;你也可以选择传递一个子级列表。以前,你必须将其定义为:

¥Consider a function that creates a new HTMLElement, calling it with no arguments generates a Div; you can optionally pass a list of children as well. Previously you would have to define it as:

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[]>;

使用泛型参数默认值,我们可以将其简化为:

¥With generic parameter defaults we can reduce it to:

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

泛型参数默认值遵循以下规则:

¥A generic parameter default follows the following rules:

  • 如果一个类型参数有一个默认值,它就被认为是可选的。

    ¥A type parameter is deemed optional if it has a default.

  • 必需的类型参数不能跟在可选的类型参数之后。

    ¥Required type parameters must not follow optional type parameters.

  • 类型参数的默认类型必须满足类型参数的约束(如果存在)。

    ¥Default types for a type parameter must satisfy the constraint for the type parameter, if it exists.

  • 指定类型参数时,只需为需要的类型参数指定类型参数即可。未指定的类型参数将解析为其默认类型。

    ¥When specifying type arguments, you are only required to specify type arguments for the required type parameters. Unspecified type parameters will resolve to their default types.

  • 如果指定了默认类型并且推断无法选择候选者,则推断默认类型。

    ¥If a default type is specified and inference cannot choose a candidate, the default type is inferred.

  • 与现有类或接口声明合并的类或接口声明可能会为现有类型参数引入默认值。

    ¥A class or interface declaration that merges with an existing class or interface declaration may introduce a default for an existing type parameter.

  • 与现有类或接口声明合并的类或接口声明可以引入新的类型参数,只要它指定默认值即可。

    ¥A class or interface declaration that merges with an existing class or interface declaration may introduce a new type parameter as long as it specifies a default.

新的 --strict master 选项

¥New --strict master option

TypeScript 中添加的新检查通常默认关闭,以避免破坏现有项目。虽然避免破坏是一件好事,但这种策略的缺点是,选择最高级别的类型安全变得越来越复杂,并且这样做需要在每次 TypeScript 版本发布时明确选择加入。使用 strict 选项,可以选择最大类型安全性,但要理解,随着改进的类型检查功能的添加,新版本的编译器可能会报告更多错误。

¥New checks added to TypeScript are often off by default to avoid breaking existing projects. While avoiding breakage is a good thing, this strategy has the drawback of making it increasingly complex to choose the highest level of type safety, and doing so requires explicit opt-in action on every TypeScript release. With the strict option it becomes possible to choose maximum type safety with the understanding that additional errors might be reported by newer versions of the compiler as improved type checking features are added.

新的 strict 编译器选项代表了多种类型检查选项的推荐设置。具体来说,指定 strict 相当于指定以下所有选项(将来可能会包含更多选项):

¥The new strict compiler option represents the recommended setting of a number of type checking options. Specifically, specifying strict corresponds to specifying all of the following options (and may in the future include more options):

确切地说,strict 选项设置了上面列出的编译器选项的默认值。这意味着仍然可以单独控制选项。例如,

¥In exact terms, the strict option sets the default value for the compiler options listed above. This means it is still possible to individually control the options. For example,

sh
--strict --noImplicitThis false

会启用除 noImplicitThis 选项之外的所有严格选项。使用此方案,可以表达由所有严格选项(除一些明确列出的选项外)组成的配置。换句话说,现在可以默认使用最高级别的类型安全,但可以选择不进行某些检查。

¥has the effect of turning on all strict options except the noImplicitThis option. Using this scheme it is possible to express configurations consisting of all strict options except some explicitly listed options. In other words, it is now possible to default to the highest level of type safety but opt out of certain checks.

从 TypeScript 2.3 开始,tsc --init 生成的默认 tsconfig.json"compilerOptions" 部分包含一个 "strict": true 设置。因此,使用 tsc --init 启动的新项目将默认启用最高级别的类型安全。

¥Starting with TypeScript 2.3, the default tsconfig.json generated by tsc --init includes a "strict": true setting in the "compilerOptions" section. Thus, new projects started with tsc --init will by default have the highest level of type safety enabled.

增强 --init 输出

¥Enhanced --init output

除了默认启用 strict 之外,tsc --init 还增强了输出。由 tsc --init 生成的默认 tsconfig.json 文件现在包含一组常用编译器选项及其注释掉的描述。只需取消注释你想要设置的配置即可获得所需的行为;我们希望新的输出能够简化新项目的设置,并在项目发展过程中保持配置文件的可读性。

¥Along with setting strict on by default, tsc --init has an enhanced output. Default tsconfig.json files generated by tsc --init now include a set of the common compiler options along with their descriptions commented out. Just un-comment the configuration you like to set to get the desired behavior; we hope the new output simplifies the setting up new projects and keeps configuration files readable as projects grow.

带有 --checkJs 的 .js 文件中的错误

¥Errors in .js files with --checkJs

默认情况下,TypeScript 编译器不会在 .js 文件中报告任何错误,包括使用 allowJs。在 TypeScript 2.3 中,类型检查错误也可以通过 checkJs.js 文件中报告。

¥By default the TypeScript compiler does not report any errors in .js files including using allowJs. With TypeScript 2.3 type-checking errors can also be reported in .js files with checkJs.

你可以通过在某些文件中添加 // @ts-nocheck 注释来跳过检查;相反,你可以选择仅检查少数 .js 文件,只需在文件中添加 // @ts-check 注释即可,而无需设置 checkJs。你还可以通过在前一行添加 // @ts-ignore 来忽略特定行上的错误。

¥You can skip checking some files by adding // @ts-nocheck comment to them; conversely you can choose to check only a few .js files by adding // @ts-check comment to them without setting checkJs. You can also ignore errors on specific lines by adding // @ts-ignore on the preceding line.

.js 文件仍会接受检查,以确保它们仅包含标准 ECMAScript 特性;类型注释仅允许在 .ts 文件中使用,并在 .js 文件中标记为错误。JSDoc 注释可用于向你的 JavaScript 代码添加一些类型信息,有关受支持的 JSDoc 构造的更多详细信息,请参阅 JSDoc 支持文档

¥.js files are still checked to ensure that they only include standard ECMAScript features; type annotations are only allowed in .ts files and are flagged as errors in .js files. JSDoc comments can be used to add some type information to your JavaScript code, see JSDoc Support documentation for more details about the supported JSDoc constructs.

有关详细信息,请参阅 JavaScript 文件的类型检查文档

¥See Type checking JavaScript Files documentation for more details.