这是一种奇怪的行为! 事实上,只有sealExhibit包含了undefined值,但是它却让lion也含有了undefined值。

得益于Jack Bates提交的,这个问题已经被修复了,它改进了TypeScript 3.9中的类型推断流程。 上面的例子中已经不再产生错误。 如果你在旧版本的TypeScript中被Promise的这个问题所困扰,我们建议你尝试一下3.9版本!

如果你一直关注TypeScript,那么你可能会注意到一个新的类型运算符awaited。 这个类型运算符的作用是准确地表达JavaScript中Promise的工作方式。

我们原计划在TypeScript 3.9中支持awaited,但在现有的代码中测试过该特性后,我们发现还需要进行一些设计,以便让所有人能够顺利地使用它。 因此,我们从主分支中暂时移除了这个特性。 我们将继续试验这个特性,它不会被包含进本次发布。

TypeScript 3.9提供了多项速度优化。 TypeScript在material-uistyled-components代码包中拥有非常慢的编辑速度和编译速度。在发现了这点后,TypeScript团队集中了精力解决性能问题。 TypeScript优化了大型联合类型、交叉类型、有条件类型和映射类型。

上面列出的每一个PR都能够减少5-10%的编译时间(对于某些代码库)。 对于material-ui库而言,现在能够节约大约40%的编译时间!

我们还调整了在编辑器中的文件重命名功能。 从Visual Studio Code团队处得知,当重命名一个文件时,计算出需要更新的import语句要花费5到10秒的时间。 TypeScript 3.9通过解决了这个问题。

尽管仍有优化的空间,我们希望当前的改变能够为每个人带来更流畅的体验。

设想一下,我们正在使用TypeScript编写一个代码库,它对外开放了一个公共函数doStuff。 该函数的类型声明了它接受两个string类型的参数,因此其它TypeScript的用户能够看到类型检查的结果,但该函数还进行了运行时的检查以便JavaScript用户能够看到一个有帮助的错误。

  1. function doStuff(abc: string, xyz: string) {
  2. assert(typeof abc === "string");
  3. assert(typeof xyz === "string");
  4. // do some stuff
  5. }

如果有人错误地使用了该函数,那么TypeScript用户能够看到红色的波浪线和错误提示,JavaScript用户会看到断言错误。 然后,我们想编写一条单元测试来测试该行为。

  1. expect(() => {
  2. doStuff(123, 456);
  3. }).toThrow();

不巧的是,如果你使用TypeScript来编译单元测试,TypeScript会提示一个错误!

  1. doStuff(123, 456);
  2. // ~~~
  3. // 错误:类型'number'不能够赋值给类型'string'。

这就是TypeScript 3.9添加了// @ts-expect-error注释的原因。 当一行代码带有// @ts-expect-error注释时,TypeScript不会提示上例的错误; 但如果该行代码没有错误,TypeScript会提示没有必要使用// @ts-expect-error

示例,以下的代码是正确的:

  1. // @ts-expect-error
  2. console.log(47 * "octopus");

会产生错误:

  1. 未使用的 '@ts-expect-error' 指令。

非常感谢Josh Goldberg实现了这个功能。 更多信息请参考。

ts-ignore 还是 ts-expect-error?

某些情况下,和// @ts-ignore是相似的,都能够阻止产生错误消息。 两者的不同在于,如果下一行代码没有错误,那么// @ts-ignore不会做任何事。

你可能会想要抛弃// @ts-ignore注释转而去使用// @ts-expect-error,并且想要知道哪一个更适用于以后的代码。 实际上,这完全取决于你和你的团队,下面列举了一些具体情况。

如果满足以下条件,那么选择ts-expect-error

  • 你在编写单元测试,并且想让类型系统提示错误
  • 你知道此处有问题,并且很快会回来改正它,只是暂时地忽略该错误
  • 你的团队成员都很积极,大家想要在代码回归正常后及时地删除忽略类型检查注释

如果满足以下条件,那么选择ts-ignore

  • 项目规模较大,产生了一些错误但是找不到相应代码的负责人
  • 正处于TypeScript版本升级的过程中,某些错误只在特定版本的TypeScript中存在,但是在其它版本中并不存在
  • 你没有足够的时间考虑究竟应该使用// @ts-ignore还是// @ts-expect-error

在TypeScript 3.7中,我们引入了未进行函数调用的检查,当你忘记去调用某个函数时会产生错误。

  1. // ...
  2. }
  3. // Oops!
  4. if (hasImportantPermissions) {
  5. // ~~~~~~~~~~~~~~~~~~~~~~~
  6. // 这个条件永远返回true,因为函数已经被定义。
  7. // 你是否想要调用该函数?
  8. deleteAllTheImportantFiles();
  9. }

然而,这个错误只会在if条件语句中才会提示。 多亏了提交的PR,现在这个特性也支持在三元表达式中使用,例如cond ? trueExpr : falseExpr

  1. declare function listFilesOfDirectory(dirPath: string): string[];
  2. declare function isDirectory(): boolean;
  3. function getAllFiles(startFileName: string) {
  4. const result: string[] = [];
  5. traverse(startFileName);
  6. return result;
  7. function traverse(currentPath: string) {
  8. return isDirectory
  9. ? // ~~~~~~~~~~~
  10. // 该条件永远返回true
  11. // 因为函数已经被定义。
  12. // 你是否想要调用该函数?
  13. listFilesOfDirectory(currentPath).forEach(traverse)
  14. : result.push(currentPath);
  15. }
  16. }

TypeScript编译器不但支持在大部分编辑器中编写TypeScript代码,还支持着在Visual Studio系列的编辑器中编写JavaScript代码。 针对不同的编辑器,在使用TypeScript/JavaScript的新功能时可能会有所区别,但是

在使用了CommonJS模块的JavaScript文件中,我们对自动导入功能进行了一个非常棒的改进。

在旧的版本中,TypeScript总是假设你想要使用ECMAScript模块风格的导入语句,并且无视你的文件类型。

  1. import * as fs from "fs";

然而,在编写JavaScript文件时,并不总是想要使用ECMAScript模块风格。 非常多的用户仍然在使用CommonJS模块,例如require(...)

更新信息请参考PR.

Code Actions 保留换行符

TypeScript的重构工具和快速修复工具对换行符的处理不是非常好。 一个基本的示例如下。

  1. const maxValue = 100;
  2. /*start*/
  3. for (let i = 0; i <= maxValue; i++) {
  4. // First get the squared value.
  5. // Now print the squared value.
  6. console.log(square);
  7. }
  8. /*end*/

如果我们选中从/*start*//*end*/,然后进行“提取到函数”操作,我们会得到如下的代码。

  1. const maxValue = 100;
  2. printSquares();
  3. function printSquares() {
  4. for (let i = 0; i <= maxValue; i++) {
  5. // First get the squared value.
  6. let square = i ** 2;
  7. // Now print the squared value.
  8. console.log(square);
  9. }
  10. }

这不是我们想要的 - 在for循环中,每条语句之间都有一个空行,但是重构后它们被移除了! TypeScript 3.9调整后,它会保留我们编写的代码。

  1. const maxValue = 100;
  2. printSquares();
  3. function printSquares() {
  4. for (let i = 0; i <= maxValue; i++) {
  5. // First get the squared value.
  6. let square = i ** 2;
  7. // Now print the squared value.
  8. console.log(square);
  9. }
  10. }

在TypeScript 3.9中,将循环提取到函数时,会保留一个换行符。

更多信息请参考PR

有时候,我们可能忘记在函数的最后添加返回值语句,尤其是在将简单箭头函数转换成还有花括号的箭头函数时。

  1. // before
  2. let f1 = () => 42;
  3. // oops - not the same!
  4. let f2 = () => {
  5. 42;
  6. };

感谢开源社区的的PR,TypeScript提供了快速修复功能来添加return语句,删除花括号,或者为箭头函数体添加小括号用以区分对象字面量。

支持”Solution Style”的tsconfig.json文件

编译器需要知道一个文件被哪个配置文件所管理,因此才能够应用适当的配置选项并且计算出当前“工程”包含了哪些文件。 在默认情况下,编辑器使用TypeScript语言服务来向上遍历父级目录以查找tsconfig.json文件。

有一种特殊情况是tsconfig.json文件仅用于引用其它tsconfig.json文件。

这个文件除了用来管理其它项目的配置文件之外什么也没做,在某些环境中它被叫作“solution”。 这里,任何一个tsconfig.*.json文件都不会被TypeScript语言服务所选用,但是我们希望语言服务能够分析出当前的.ts文件被上述tsconfig.json中引用的哪个配置文件所管理。