1,142
社区成员
TypeScript 是一种用于应用程序级 JavaScript 的语言。JavaScript 作为 Java 的补充,旨在用于创建以网络为中心的应用程序,是一种轻量级的解释型编程语言,尽管它很容易实现,但 JavaScript 并不能算做是一个成熟的编程语言,“客户端 JavaScript 不允许读取或写入文件、不能用于网络应用程序、没有任何多线程或多处理器功能”是它最大的短板,由于其自身局限性导致 JavaScript 难以胜任和维护大型项目的开发,TypeScript 从此应运而生。
TypeScript 通过在 JavaScript 的基础上添加静态类型定义构建而成,它可以通过 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<boolean> { return false; } async function bar(): Promise<string> { const fooResult = foo(); if (fooResult) { // <- error! :D return "true"; } return "false"; }
但是下面的代码不会
async function foo(): Promise<boolean> { return false; } async function bar(): Promise<string> { 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 以上功能的详细信息,请查看官方发布公告。
直接交流,扫码入群: