TypeScript 4.4 正式发布

在TypeScript之前,受Java、C、C++等静态类型语言的影响,大多数人对于静态类型的印象是刻板又啰嗦,而且编译流程极其繁琐。但是在深入了解TypeScript之后,就会发现它既拥有静态类型的优点,比如自编译能力、编译时的强类型、模块化等,同时代码又能够像JavaScript一样简洁灵活,深受广大开发者的喜爱。

近日,微软官方发布了 TypeScript 4.4 版本,开发者可以通过 NuGet 开始使用或者通过以下命令进行安装:

npm install typescript

本次更新亮点包括:

  • 提供针对 Aliased Conditions 和 Discriminants 的控制流程分析
  • 增加 symbol 类型和模板字符串模式的索引签名
  • 在 Catch 中的变量默认为 unknow 类型(--useUnknownInCatchVariables)
  • 准确的可选属性类型(--exactOptionalPropertyTypes)
  • 静态块类
  • 针对 tsc --help 的更新和改进
  • 性能优化
  • JavaScript 的拼写建议
  • 嵌入提示
  • 重大变化

本文将对大家普遍关心的“性能优化”及“重大变化”两个方面进行详细解读。

性能优化

更快的声明

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

针对 Big Ouputs 的更快源映射生成

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)();

在 Catch 变量中使用 unknown

使用 --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

更广泛的“Always-Truthy”承诺检查

在以前的版本中,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 以上功能的详细信息,请查看官方发布公告

你可能感兴趣的:(typescript,java)