在TypeScript之前,受Java、C、C++等静态类型语言的影响,大多数人对于静态类型的印象是刻板又啰嗦,而且编译流程极其繁琐。但是在深入了解TypeScript之后,就会发现它既拥有静态类型的优点,比如自编译能力、编译时的强类型、模块化等,同时代码又能够像JavaScript一样简洁灵活,深受广大开发者的喜爱。
近日,微软官方发布了 TypeScript 4.4 版本,开发者可以通过 NuGet 开始使用或者通过以下命令进行安装:
npm install typescript
本次更新亮点包括:
本文将对大家普遍关心的“性能优化”及“重大变化”两个方面进行详细解读。
TypeScript 现在会缓存内部符号在不同上下文中是否可以访问以及特定类型应该如何打印。这些更改可以提高 TypeScript 在具有相当复杂类型代码中的整体性能,尤其是在 declaration 下标记 .d.ts 的文件。
TypeScript 经常需要对文件路径进行几种类型的“规范化”,以使它们成为编译器可以在任何地方都能使用的统一格式。这包括用斜杠替换反斜杠,或者删除中间 /./
和 /../
路径段。当 TypeScript 在数百万条路径上运行时,这些操作可能会变得有点慢。在 TypeScript 4.4 中,路径首先要进行快速检查,查看它们是否需要规范化。在大型项目中,这些改进总共减少了 5-10% 的项目加载时间,而在我们内部测试过的大型项目中,这些改进显著地减少了更多的加载时间。
TypeScript 现在缓存了它构建路径映射的方式(在 tsconfig.json
中使用 paths
选项 ),对于具有数百个映射的项目,这种减少是非常显著的。
--strict
进行更快的增量编译这实际上是一个 Bug,如果启用了 --strict
TypeScript 最终会在 --incremental
编译下重做类型检查工作,这就导致了许多构建就像关闭 --incremental
一样缓慢。TypeScript 4.4 修复了这个问题,同时这个修改也被反向移植到了 TypeScript 4.3 中。
TypeScript 4.4 为超大输出文件的源映射生成添加了优化功能,在构建旧版本的 TypeScript 编译器时,可使 emit 时间减少 8% 左右。
--force
进行更快的编译在项目引用上使用 --build
模式时,TypeScript 必须执行最新的检查以确定有哪些文件需要重建。但是,在执行 --force
构建时,该信息又是无关紧要的,因为每个项目的依赖项都将从头开始构建。在 TypeScript 4.4 中,--force
构建避免了那些不必要的步骤然后开始完整构建。
在 TypeScript 的早期版本中,调用来自 CommonJS、AMD 和其他 非ES 模块系统的导入会设置被调用函数的 this
值。具体来说,在以下示例中,当调用 fooModule.foo()
时,foo()
方法将把fooModule
设置为 this
值。
// Imagine this is our imported module, and it has an export named 'foo'.
let fooModule = {
foo() {
console.log(this);
}
};
fooModule.foo();
当我们调用 ECMAScript 导出的函数时,这不是它们应该工作的方式。这就是为什么TypeScript 4.4 会在调用导入函数时使用下面的 emit,会故意丢弃 this
值。
// Imagine this is our imported module, and it has an export named 'foo'.
let fooModule = {
foo() {
console.log(this);
}
};
// Notice we're actually calling '(0, fooModule.foo)' now, which is subtly different.
(0, fooModule.foo)();
使用 --strict
标志运行的用户可能会看到关于 catch
变量 unknow
的新错误,特别是在现有代码假定只捕获了 Error
值的情况下。这通常会导致如下错误消息:
Property 'message' does not exist on type 'unknown'.
Property 'name' does not exist on type 'unknown'.
Property 'stack' does not exist on type 'unknown'.
为了解决这个问题,你可以专门添加运行时检查,以确保抛出的类型与期望的类型相匹配。如果不这样的话,你只能使用类型断言,在 catch 变量中添加一个显式的:any,或者关闭 --
useUnknownInCatchVariables
。
在以前的版本中,TypeScript 引入了“Always truth promise check”来捕捉可能被遗忘的 await
代码;然而,检查只对命名声明有效。这也就意味着,这段代码将正确地接收一个错误……
async function foo(): Promise {
return false;
}
async function bar(): Promise {
const fooResult = foo();
if (fooResult) { // <- error! :D
return "true";
}
return "false";
}
但是下面的代码不会
async function foo(): Promise {
return false;
}
async function bar(): Promise {
if (foo()) { // <- no error :(
return "true";
}
return "false";
}
以下代码是一个错误,因为抽象属性可能没有初始值设定项:
abstract class C {
abstract prop = 1;
// ~~~~
// Property 'prop' cannot have an initializer because it is marked abstract.
}
相反,你只能为属性指定一个类型:
abstract class C {
abstract prop: number;
}
关于 TypeScript 4.4 以上功能的详细信息,请查看官方发布公告。