TypeScript 2.3

ES5/ES3 的生成器和迭代

🌐 Generators and Iteration for ES5/ES3

首先一些 ES2016 术语:

🌐 First some ES2016 terminology:

迭代器

🌐 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 方法,那么这个对象就被称为“可迭代的”。

🌐 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.

迭代器协议还定义了一些 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 还引入了“生成器”(Generators),生成器是可以通过 Iterator 接口和 yield 关键字返回部分计算结果的函数。生成器还可以通过 yield * 在内部将调用委托给另一个可迭代对象。例如:

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 时,编译器会采用新的类型检查和生成行为,如果在被迭代的对象上找到 [Symbol.iterator]() 方法,则尝试调用它;如果未找到,则在对象上创建一个合成的数组迭代器。

🌐 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。

for..of 语句、数组解构以及数组、调用和新建表达式中的扩展元素在使用 downlevelIteration 时,如果可用,在 ES5/E3 中支持 Symbol.iterator,但是即使在运行时或设计时数组未定义 Symbol.iterator,也可以使用它们。

异步迭代

🌐 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 方法,则称该对象为“可迭代”的。

🌐 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 的填充),此外还需要一个有效的 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");
  • 你还需要在你的 lib 选项中包含 esnext,以获取 AsyncIterator 声明,如果你还没有的话。
  • 最后,如果你的目标是 ES5 或 ES3,你还需要设置 --downlevelIterators 标志。

泛型参数默认值

🌐 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:

  • 如果一个类型参数有一个默认值,它就被认为是可选的。
  • 必需的类型参数不能跟在可选的类型参数之后。
  • 类型参数的默认类型必须满足类型参数的约束(如果存在)。
  • 在指定类型参数时,只需要为必需的类型参数指定类型参数。未指定的类型参数将解析为它们的默认类型。
  • 如果指定了默认类型并且推断无法选择候选者,则推断默认类型。
  • 与现有类或接口声明合并的类或接口声明可能会为现有类型参数引入默认值。
  • 与现有类或接口声明合并的类或接口声明可以引入新的类型参数,只要它指定默认值即可。

新的 --strict 主选项

🌐 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.

.js 文件中的 --checkJs 错误

🌐 Errors in .js files with --checkJs

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

🌐 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 注释来跳过检查;相反,你也可以通过在文件中添加 // @ts-check 注释来只检查少数 .js 文件,而无需设置 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 支持文档

有关更多详情,请参阅 Type checking JavaScript Files 文档

🌐 See Type checking JavaScript Files documentation for more details.