更严格的生成器
¥Stricter Generators
TypeScript 3.6 对迭代器和生成器函数进行了更严格的检查。在早期版本中,生成器的用户无法区分一个值是生成器产生的还是返回的。
¥TypeScript 3.6 introduces stricter checking for iterators and generator functions. In earlier versions, users of generators had no way to differentiate whether a value was yielded or returned from a generator.
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
。
¥Additionally, generators just assumed the type of yield
was always 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()
时正确地报错。这要归功于 Iterator
和 IteratorResult
类型声明中的一些更改,它们包含了一些新的类型参数,以及 TypeScript 用于表示生成器的新类型 Generator
类型。
¥In TypeScript 3.6, the checker now knows that the correct type for curr.value
should be string
in our first example, and will correctly error on our call to next()
in our last example.
This is thanks to some changes in the Iterator
and IteratorResult
type declarations to include a few new type parameters, and to a new type that TypeScript uses to represent generators called the Generator
type.
Iterator
类型现在允许用户指定生成的类型、返回类型以及 next
可以接受的类型。
¥The Iterator
type now allows users to specify the yielded type, the returned type, and the type that next
can accept.
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
类型是一个 Iterator
,它始终同时包含 return
和 throw
方法,并且也是可迭代的。
¥Building on that work, the new Generator
type is an Iterator
that always has both the return
and throw
methods present, and is also iterable.
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>;}
为了区分返回值和产生的值,TypeScript 3.6 将 IteratorResult
类型转换为可区分联合类型:
¥To allow differentiation between returned values and yielded values, TypeScript 3.6 converts the IteratorResult
type to a 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;}
简而言之,这意味着你在直接处理迭代器中的值时,能够适当地缩小其范围。
¥In short, what this means is that you’ll be able to appropriately narrow down values from iterators when dealing with them directly.
为了正确表示调用 next()
时可以传递给生成器的类型,TypeScript 3.6 还会推断生成器函数主体中 yield
的某些用法。
¥To correctly represent the types that can be passed in to a generator from calls to next()
, TypeScript 3.6 also infers certain uses of yield
within the body of a generator function.
ts
function* foo() {let x: string = yield;console.log(x.toUpperCase());}let x = foo();x.next(); // first call to 'next' is always ignoredx.next(42); // error! 'number' is not assignable to 'string'
如果你希望明确指定,还可以使用显式返回类型来强制指定 yield
表达式可以返回、产生和求值的值的类型。下面的 next()
只能通过 boolean
调用,并且根据 done
的值,value
是 string
还是 number
。
¥If you’d prefer to be explicit, you can also enforce the type of values that can be returned, yielded, and evaluated from yield
expressions using an explicit return type.
Below, next()
can only be called with boolean
s, and depending on the value of done
, value
is either a string
or a number
.
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!
更多变更详情,请访问 查看此处的拉取请求。
¥For more details on the change, see the pull request here.
更精确的数组展开
¥More Accurate Array Spread
在 ES2015 之前的目标版本中,像 for
/of
循环和数组展开这样的结构,其最可靠的输出可能会有点繁重。因此,TypeScript 默认使用更简单的 emit,它仅支持数组类型,并支持使用 downlevelIteration
标志迭代其他类型。不使用 downlevelIteration
的更宽松的默认值运行良好;然而,在一些常见情况下,数组展开的转换存在明显的差异。例如,以下包含扩展的数组
¥In pre-ES2015 targets, the most faithful emit for constructs like for
/of
loops and array spreads can be a bit heavy.
For this reason, TypeScript uses a simpler emit by default that only supports array types, and supports iterating on other types using the downlevelIteration
flag.
The looser default without downlevelIteration
works fairly well; however, there were some common cases where the transformation of array spreads had observable differences.
For example, the following array containing a spread
ts
[...Array(5)];
可以重写为以下数组字面量
¥can be rewritten as the following array literal
js
[undefined, undefined, undefined, undefined, undefined];
然而,TypeScript 会将原始代码转换为以下代码:
¥However, TypeScript would instead transform the original code into this code:
ts
Array(5).slice();
略有不同。Array(5)
生成一个长度为 5 的数组,但没有定义的属性槽。
¥which is slightly different.
Array(5)
produces an array with a length of 5, but with no defined property slots.
TypeScript 3.6 引入了一个新的 __spreadArrays
辅助类型,可以精确模拟 ECMAScript 2015 在 downlevelIteration
以外的旧目标平台上发生的情况。__spreadArrays
在 tslib 中也可用。
¥TypeScript 3.6 introduces a new __spreadArrays
helper to accurately model what happens in ECMAScript 2015 in older targets outside of downlevelIteration
.
__spreadArrays
is also available in tslib.
更多信息请见 查看相关的拉取请求!
¥For more information, see the relevant pull request.
改进了 Promises 的用户体验
¥Improved UX Around Promises
TypeScript 3.6 针对 Promise
被错误处理的情况进行了一些改进。
¥TypeScript 3.6 introduces some improvements for when Promise
s are mis-handled.
例如,在将 Promise
传递给另一个函数之前,经常忘记将其内容 .then()
或 await
化。TypeScript 的错误消息现在经过特殊处理,并会告知用户或许应该考虑使用 await
关键字。
¥For example, it’s often very common to forget to .then()
or await
the contents of a Promise
before passing it to another function.
TypeScript’s error messages are now specialized, and inform the user that perhaps they should consider using the await
keyword.
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'?}
在对 Promise
进行 await
或 .then()
修饰之前尝试访问方法也很常见。这只是众多例子中的一个,我们可以做得更好。
¥It’s also common to try to access a method before await
-ing or .then()
-ing a Promise
.
This is another example, among many others, where we’re able to do better.
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'?}
更多详情,请参阅 查看原始问题 以及链接回它的拉取请求。
¥For more details, see the originating issue, as well as the pull requests that link back to it.
更好地支持标识符的 Unicode
¥Better Unicode Support for Identifiers
TypeScript 3.6 在向 ES2015 及更高版本的目标平台发布时,对标识符中的 Unicode 字符提供了更好的支持。
¥TypeScript 3.6 contains better support for Unicode characters in identifiers when emitting to ES2015 and later targets.
ts
const 𝓱𝓮𝓵𝓵𝓸 = "world"; // previously disallowed, now allowed in '--target es2015'
import.meta
SystemJS 支持
¥import.meta
Support in SystemJS
当 module
目标设置为 system
时,TypeScript 3.6 支持将 import.meta
转换为 context.meta
。
¥TypeScript 3.6 supports transforming import.meta
to context.meta
when your module
target is set to system
.
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);},};});
get
和 set
访问器在环境上下文中可用
¥get
and set
Accessors Are Allowed in Ambient Contexts
在早期版本的 TypeScript 中,该语言不允许在环境上下文中使用 get
和 set
访问器(例如在 declare
-d 类中,或通常在 .d.ts
文件中)。理由是,就读写属性而言,访问器与属性并无区别;然而,因为 ECMAScript 的类字段提案可能与现有版本的 TypeScript 的行为不同,我们意识到我们需要一种方法来传达这种不同的行为,以便在子类中提供适当的错误。
¥In previous versions of TypeScript, the language didn’t allow get
and set
accessors in ambient contexts (like in declare
-d classes, or in .d.ts
files in general).
The rationale was that accessors weren’t distinct from properties as far as writing and reading to these properties;
however, because ECMAScript’s class fields proposal may have differing behavior from in existing versions of TypeScript, we realized we needed a way to communicate this different behavior to provide appropriate errors in subclasses.
因此,用户可以在 TypeScript 3.6 的环境上下文中编写 getter 和 setter。
¥As a result, users can write getters and setters in ambient contexts in TypeScript 3.6.
ts
declare class Foo {// Allowed in 3.6+.get x(): number;set x(val: number);}
在 TypeScript 3.7 中,编译器本身将利用此功能,以便生成的 .d.ts
文件也将触发 get
/set
访问器。
¥In TypeScript 3.7, the compiler itself will take advantage of this feature so that generated .d.ts
files will also emit get
/set
accessors.
环境类和函数可以合并
¥Ambient Classes and Functions Can Merge
在早期版本的 TypeScript 中,任何情况下合并类和函数都是错误的。现在,环境类和函数(带有 declare
修饰符的类/函数,或在 .d.ts
文件中)可以合并。这意味着你现在可以编写以下内容:
¥In previous versions of TypeScript, it was an error to merge classes and functions under any circumstances.
Now, ambient classes and functions (classes/functions with the declare
modifier, or in .d.ts
files) can merge.
This means that now you can write the following:
ts
export declare function Point2D(x: number, y: number): Point2D;export declare class Point2D {x: number;y: number;constructor(x: number, y: number);}
无需使用
¥instead of needing to use
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
合并)。
¥One advantage of this is that the callable constructor pattern can be easily expressed while also allowing namespaces to merge with these declarations (since var
declarations can’t merge with namespace
s).
在 TypeScript 3.7 中,编译器将利用此功能,以便从 .js
文件生成的 .d.ts
文件能够恰当地捕获类函数的可调用性和可构造性。
¥In TypeScript 3.7, the compiler will take advantage of this feature so that .d.ts
files generated from .js
files can appropriately capture both the callability and constructability of a class-like function.
详情请见 查看 GitHub 上的原始 PR。
¥For more details, see the original PR on GitHub.
支持 --build
和 --incremental
的 API
¥APIs to Support --build
and --incremental
TypeScript 3.0 引入了对引用其他项目并使用 --build
标志逐步构建它们的支持。此外,TypeScript 3.4 引入了 incremental
标志,用于保存先前编译的信息,以便仅重建特定文件。这些标志对于更灵活地构建项目和加快构建速度非常有用。遗憾的是,这些标志不适用于 Gulp 和 Webpack 等第三方构建工具。TypeScript 3.6 现在公开了两组 API,用于操作项目引用和增量程序构建。
¥TypeScript 3.0 introduced support for referencing other projects and building them incrementally using the --build
flag.
Additionally, TypeScript 3.4 introduced the incremental
flag for saving information about previous compilations to only rebuild certain files.
These flags were incredibly useful for structuring projects more flexibly and speeding builds up.
Unfortunately, using these flags didn’t work with 3rd party build tools like Gulp and Webpack.
TypeScript 3.6 now exposes two sets of APIs to operate on project references and incremental program building.
为了创建 incremental
版本,用户可以利用 createIncrementalProgram
和 createIncrementalCompilerHost
API。用户还可以使用新公开的 readBuilderProgram
函数从此 API 生成的 .tsbuildinfo
文件中重新填充旧程序实例,该函数仅用于创建新程序(即,你无法修改返回的实例 - 它仅用于其他 create*Program
函数中的 oldProgram
参数)。
¥For creating incremental
builds, users can leverage the createIncrementalProgram
and createIncrementalCompilerHost
APIs.
Users can also re-hydrate old program instances from .tsbuildinfo
files generated by this API using the newly exposed readBuilderProgram
function, which is only meant to be used as for creating new programs (i.e. you can’t modify the returned instance - it’s only meant to be used for the oldProgram
parameter in other create*Program
functions).
为了利用项目引用,我们公开了一个新的 createSolutionBuilder
函数,它返回新类型 SolutionBuilder
的实例。
¥For leveraging project references, a new createSolutionBuilder
function has been exposed, which returns an instance of the new type SolutionBuilder
.
更多这些 API 详情,请访问 查看原始拉取请求。
¥For more details on these APIs, you can see the original pull request.
支持分号的代码编辑
¥Semicolon-Aware Code Edits
像 Visual Studio 和 Visual Studio Code 这样的编辑器可以自动应用快速修复、重构和其他转换,例如自动从其他模块导入值。这些转换由 TypeScript 提供支持,旧版本的 TypeScript 会在每个语句的末尾无条件地添加分号;遗憾的是,这不符合许多用户的风格指南,许多用户对编辑器插入分号感到不满。
¥Editors like Visual Studio and Visual Studio Code can automatically apply quick fixes, refactorings, and other transformations like automatically importing values from other modules. These transformations are powered by TypeScript, and older versions of TypeScript unconditionally added semicolons to the end of every statement; unfortunately, this disagreed with many users’ style guidelines, and many users were displeased with the editor inserting semicolons.
TypeScript 现在足够智能,可以在应用此类编辑时检测文件是否使用分号。如果你的文件通常缺少分号,TypeScript 不会添加分号。
¥TypeScript is now smart enough to detect whether your file uses semicolons when applying these sorts of edits. If your file generally lacks semicolons, TypeScript won’t add one.
详情请见 查看相应的拉取请求。
¥For more details, see the corresponding pull request.
更智能的自动导入语法
¥Smarter Auto-Import Syntax
JavaScript 有许多不同的模块语法或约定:ECMAScript 标准中的 ,Node 已经支持的 (CommonJS)、AMD、System.js 等等!在大多数情况下,TypeScript 默认使用 ECMAScript 模块语法进行自动导入,但这在某些具有不同编译器设置的 TypeScript 项目,或使用纯 JavaScript 和 require
调用的 Node 项目中通常并不适用。
¥JavaScript has a lot of different module syntaxes or conventions: the one in the ECMAScript standard, the one Node already supports (CommonJS), AMD, System.js, and more!
For the most part, TypeScript would default to auto-importing using ECMAScript module syntax, which was often inappropriate in certain TypeScript projects with different compiler settings, or in Node projects with plain JavaScript and require
calls.
TypeScript 3.6 现在更加智能,它会在决定如何自动导入其他模块之前,检查现有的导入。你可以 在此处查看原始拉取请求中的更多详细信息。
¥TypeScript 3.6 is now a bit smarter about looking at your existing imports before deciding on how to auto-import other modules. You can see more details in the original pull request here.
新的 TypeScript Playground
¥New TypeScript Playground
TypeScript 开发环境进行了一次亟需的更新,新增了实用的功能!新的 Playground 很大程度上是 Artem Tyurin 的 TypeScript 体验区 的一个分支,社区成员越来越多地使用它。非常感谢 Artem 的帮助!
¥The TypeScript playground has received a much-needed refresh with handy new functionality! The new playground is largely a fork of Artem Tyurin’s TypeScript playground which community members have been using more and more. We owe Artem a big thanks for helping out here!
新的 Playground 现在支持许多新选项,包括:
¥The new playground now supports many new options including:
-
target
选项(允许用户从es5
切换到es3
、es2015
、esnext
等)¥The
target
option (allowing users to switch out ofes5
toes3
,es2015
,esnext
, etc.) -
所有严格性标志(包括
strict
)¥All the strictness flags (including just
strict
) -
支持纯 JavaScript 文件(使用
allowJS
和可选的checkJs
)¥Support for plain JavaScript files (using
allowJS
and optionallycheckJs
)
这些选项在分享 Playground 示例链接时也会保留,让用户更可靠地分享示例,而无需告诉接收者“哦,别忘了打开 noImplicitAny
选项!”。
¥These options also persist when sharing links to playground samples, allowing users to more reliably share examples without having to tell the recipient “oh, don’t forget to turn on the noImplicitAny
option!“.
在不久的将来,我们将更新 Playground 示例,添加 JSX 支持,并完善自动类型获取功能,这意味着你将能够在 Playground 上获得与个人编辑器相同的体验。
¥In the near future, we’re going to be refreshing the playground samples, adding JSX support, and polishing automatic type acquisition, meaning that you’ll be able to see the same experience on the playground as you’d get in your personal editor.
随着我们不断改进 Playground 和网站,我们欢迎在 GitHub 上提出反馈和拉取请求!
¥As we improve the playground and the website, we welcome feedback and pull requests on GitHub!