7 月 1 日,TypeScript 4.4 测试版发布了!
你可以通过 NuGet获取这个测试版,或者使用下面的 npm 命令获取:
NuGet:你还可以通过以下方式获得编辑器支持:
下载 Visual Studio 2019/2017
遵循 Visual Studio Code 和 Sublime Text 3 的指南。
VSCode 指南:https://code.visualstudio.com/Docs/languages/typescript#_using-newer-typescript-versions
Sublime Text 指南:https://github.com/Microsoft/TypeScript-Sublime-Plugin/#note-using-different-versions-of-typescript
TypeScript 4.4 的一些主要亮点包括:
用于 Aliased 条件的控制流分析
符号和模板字符串模式索引签名
Catch 变量中的类型默认改为 unknown(--useUnknownInCatchVariables)
精确的可选属性类型(--exactOptionalPropertyTypes)
tsc --help 更新和改进
性能改进
JavaScript 的拼写建议
嵌入提示
重大更改
下面来具体解析!
用于 Aliased 条件的控制流分析
在 JavaScript 中,我们经常需要以不同的方式探测一个变量,看看它是否有我们可以使用的某个特定类型。TypeScript 理解这些检查,并将它们称为类型保护(type guards)。类型检查器使用称为控制流分析(control flow analysis)的特性来推断每种语言构造中的类型,这样就不必在我们使用每个变量时告诉 TypeScript 这个变量的类型了。
例如,我们可以这样写
在这个例子中,我们检查了 arg 是否是一个 string。TypeScript 识别了 typeof arg === "string"这个检查,认为它是一个类型保护,并且能够确定 arg 应该是 if 块主体中的一个 string。但是,如果我们将条件移出为一个常量会发生什么?
在以前的 TypeScript 版本中,这将抛出一个错误——即使 argIsString 被分配了类型保护的值也是如此,TypeScript 把这些信息丢掉了。这会很麻烦,因为我们可能想在多个位置重复使用相同的检查。为了解决这个问题,用户往往需要重复自己做过的事情或使用类型断言(cast)。在 TypeScript 4.4 中情况不再如此了。上面的例子不会再抛出错误!当 TypeScript 看到我们正在测试一个常量值时,它会做一些额外的工作来看看它是否包含类型保护。如果这个类型保护对 const、readonly 属性或未修改的参数进行操作,则 TypeScript 能够适当地缩小该值。
新版保留了各种类型保护条件——不仅仅是 typeof 检查。例如,可辨识联合类型现在很容易检查了。
再举一个例子,下面是一个检查它的两个输入是否有内容的函数。
如果 mustDoWork 为 true,TypeScript 就可以理解 inputA 和 inputB 都存在。这意味着我们不必编写像 inputA!这样的非空断言来告诉 TypeScript,inputA 不是 undefined 的。这里很好用的一点是这种分析是可传递的。如果我们将一个常量分配给一个包含更多常量的条件,并且这些常量都被分配了类型保护,那么 TypeScript 可以稍后传播这些条件。
请注意,这里有一个限制——TypeScript 在检查这些条件时不会一直深入下去,但它的分析对于大多数检查来说都足够深了。这个特性应该可以让很多直观的 JavaScript 代码在 TypeScript 中“正常运行”,不至于妨碍你的工作。要了解更多细节,请查看 GitHub 上的实现!
符号和模板字符串模式索引签名
TypeScript 允许我们使用索引签名(index signature)来描述每个属性都必须有特定类型的对象。这允许我们将这些对象用作类似字典的类型,我们可以在其中使用字符串键,通过方括号对它们进行索引。
例如,我们可以编写一个带有索引签名的类型,该类型接受 string 键并映射到 boolean 值。如果我们尝试分配非 boolean 的值,会抛出一个错误。
虽然 Map 在这里可能是更好的数据结构(特别是 Map<string, boolean>),但 JavaScript 对象用起来一般更方便,或者恰好是我们可以使用的对象。类似地,Array<T>已经定义了一个 number 索引签名,它允许我们插入/检索 T 类型的值。
需要在各种地方表达大量代码的时候,索引签名非常有用;然而到目前为止,它们仅限于 string 和 number 键(并且 string 索引签名有一个人为的怪癖,它们可以接受 number 键,因为无论如何它们都会被强制转换为字符串)。这意味着 TypeScript 不允许使用 symbol 键索引对象。TypeScript 也无法对某些 string 键的子集建模索引签名——例如一个只描述“名称以文本 data-开头”的属性的索引签名。TypeScript 4.4 解决了这些限制,并允许 symbol 和模板字符串模式的索引签名。
例如,TypeScript 现在允许我们声明可以在任意 symbol 上 keyed 的类型。
类似地,我们可以使用模板字符串模式类型编写索引签名。这种做法的一种可能用途是从 TypeScript 的多余属性检查中排除以 data-开头的属性。当我们将一个对象字面量传递给具有预期类型的内容时,TypeScript 将查找未在预期类型中声明的多余属性。
关于索引签名最后还要提一下,它们现在允许联合类型,只要它们是无限域原始类型的联合即可——特别是:
string
number
symbol
模板字符串模式(例如
hello-${string}
)
如果一个索引签名的参数是这些类型的联合,它将脱糖为几个不同的索引签名。
要了解更多细节,请阅读拉取请求。
Catch 变量中的类型默认改为 unknown(--useUnknownInCatchVariables)
在 JavaScript 中,任何类型的值都可以用 throw 抛出并在一个 catch 子句中捕获。因此,TypeScript 过去将 catch 子句变量类型化为 any,并且不允许其他任何类型注解:
一旦 TypeScript 添加了 unknown 类型,很明显,对于想要最高程度正确性和类型安全性的用户来说,unknown 是比 catch 子句变量中的 any 更好的选项,因为它可以更好地缩小范围并迫使我们针对任意值进行测试。最终,TypeScript 4.0 允许用户在每个 catch 子句变量上指定一个 unknown(或 any)的显式类型注解,这样我们就可以根据具体情况选择更严格的类型;然而,对于某些人来说,在每个 catch 子句上手动指定:unknown 是一件苦差事。所以 TypeScript 4.4 引入了一个名为--useUnknownInCatchVariables 的新标志。此标志将 catch 子句变量的默认类型从 any 更改为 unknown。
这个标志在--strict 选项系列下启用。这意味着如果你使用--strict 检查代码,此选项将自动打开。你可能会在 TypeScript 4.4 中遇到下面这样的错误:
如果我们不想在 catch 子句中处理 unknown 变量,我们随时可以添加一个显式的: any 注解,这样我们就可以不用更严格的类型了。
要了解更多细节,请查看实现的拉取请求。
精确的可选属性类型(--exactOptionalPropertyTypes)
在 JavaScript 中,读取一个对象上缺失的一个属性会产生 undefined 值。也可能有一个实际属性的值为 undefined。JavaScript 中的许多代码倾向以相同的方式处理这些情况,因此一开始 TypeScript 只是解释每个可选属性,就好像用户在类型中编写了 undefined 一样。例如,
被认为等价于
这意味着用户可以显式用 undefined 代替 age。
因此默认情况下,TypeScript 不会区分值为 undefined 的存在的属性和缺失的属性。虽然这在大多数情况下都不会出问题,但并非所有 JavaScript 代码都做出了相同的假设。Object.assign、Object.keys、objectspread({...obj})和 for-in 循环等函数和运算符的行为取决于对象上是否实际存在属性。在我们的 Person 示例中,如果在 age 的存在与否影响很大的上下文中观察到了 age 属性,这就可能会导致运行时错误。在 TypeScript 4.4 中新加入的标志--exactOptionalPropertyTypes 指定了可选属性类型应完全按照编写的方式来解释,这意味着|undefined 不会添加到类型中:
这个标志不是--strict 系列的一部分,如果你想要这种行为,需要显式打开它。它还需要启用--strictNullChecks。我们将更新 DefinitelyTyped 和其他一些定义,设法让过渡过程尽可能简单一些,但你还是可能会遇到一些摩擦,具体取决于你的代码结构。要了解更多细节,你可以在此处查看实现的拉取请求。
tsc --help 更新和改进
TypeScript 的--help 选项得到了更新!感谢 Song Gao 的工作,我们带来了一些更改,包括更新编译器选项的描述,并使用一些颜色和其他视觉分离途径重新设计了--help 菜单的样式。虽然我们仍在对样式做一些迭代,来让那些跨平台默认主题可以正常工作,但你可以查看原始提案来提前了解它的样貌。
原始提案:https://github.com/microsoft/TypeScript/issues/44074
性能改进
更快的声明发射
TypeScript 现在会缓存内部符号是否可在不同上下文中访问的相关信息,以及特定类型应如何打印的相关信息。这些更改可以提高有着相当复杂类型的代码的性能表现,尤其是在--declaration 标志下发射.d.ts 文件时性能提升非常明显。
在此处了解更多细节。
更快的路径规范化
TypeScript 通常必须对文件路径进行多种类型的“规范化”,将它们转换为可以让编译器随处使用的一致格式。这种操作涉及用斜杠替换反斜杠,或删除路径的中间/./和/../段之类的事情。当 TypeScript 处理数以百万的文件路径时,这种规范化操作往往速度会很慢。在 TypeScript 4.4 中,路径首先要进行快速检查,查看它们究竟是否需要规范化操作。这些改进加在一起,将大型项目的加载时间减少了 5-10%,而在我们内部测试的大型项目中加载时间减少得更多。
要了解更多细节,你可以查看路径段规范化的PR以及斜杠规范化的PR。
更快的路径映射
TypeScript 现在会缓存它构建路径映射的方式(使用 tsconfig.json 中的 paths 选项)。对于具有数百个映射的项目,速度提升是很明显的。在这里可以了解更多细节。
使用--strict 加快增量构建
在一个错误中,如果--strict 处于启用状态,TypeScript 最终会在--incremental 编译下重做类型检查工作。这拖慢了许多构建的速度,好像它们关闭了--incremental 一样。TypeScript 4.4 修复了这个问题,但该更改也已向后移植到了 TypeScript 4.3。
在这里查看更多细节。
为大输出更快地生成源图
TypeScript 4.4 优化了超大输出文件的源映射生成速度。在构建旧版本的 TypeScript 编译器时,这一优化将发射时间减少了约 8%。
我们要向 David Michon 表示感谢,他提供了一个简单而干净的更改来实现这一性能优化。
更快的--force 构建
在项目引用上使用--build 模式时,TypeScript 必须执行更新检查以确定需要重建哪些文件。然而,当执行--force 构建时,这一信息实际上是无关紧要的,因为每个项目依赖项都将被从头开始重建。在 TypeScript 4.4 中,--force 构建避免了那些不必要的步骤,直接开始一个完整构建。在此处查看有关这一更改的更多细节。
JavaScript 的拼写建议
TypeScript 改善了 Visual Studio 和 Visual Studio Code 等编辑器中的 JavaScript 编辑体验。大多数情况下,TypeScript 试图避开 JavaScript 文件;然而,TypeScript 往往收集了很多信息,足以提出不错的建议,它也有不少方法可以提出不太有侵略性的建议。
于是 TypeScript 现在会在纯 JavaScript 文件中发出拼写建议——没有// @ts-check,或关闭了 checkJs 的项目中都会提出建议。这些建议的形式和 TypeScript 已有的“Did you mean…?”是一样的,现在它们在所有 JavaScript 文件中都可用。
这些拼写建议可以提供一个不那么干扰人的线索,告诉你你的代码是错误的。在测试这一特性时,我们设法在现有代码中找到了一些错误!
有关这一新特性的更多细节,请查看拉取请求!
嵌入提示
TypeScript 正在试验对嵌入文本(inlay text)的编辑器支持,这样就可以在代码中内联显示一些有用的信息,例如参数名称。你可以将其视为一种友好的“幽灵文本”。
Visual Studio Code 中的嵌入提示预览
此特性由 Wenlu Wang 构建,他的拉取请求有更多细节。你可以在此处跟踪我们将该特性与 Visual Studio Code 集成的进度。
拉取请求:https://github.com/microsoft/TypeScript/pull/42089
跟踪进度:https://github.com/microsoft/vscode/pull/113412
重大更改
针对 TypeScript 4.4 的 lib.d.ts 更改
与每个 TypeScript 版本一样,lib.d.ts 的声明(尤其是为 Web 上下文生成的声明)已更改。你可以查阅我们的已知 lib.dom.d.ts 更改列表以了解受影响的内容。
在 Catch 变量中使用 unknown
从技术上讲,使用--strict 标志运行的用户可能会看到一些关于 catch 变量是 unknown 的新错误,特别是在现有代码假定只捕获了 Error 值的时候。这通常会导致下面这样的错误消息:
为了解决这个问题,你可以专门添加运行时检查以确保抛出的类型与你的预期类型相匹配。或者你可以只使用一个类型断言,向你的 catch 变量添加一个显式: any,或者关闭--useUnknownInCatchVariables。
抽象属性不允许初始化器
以下代码现在会抛出一个错误,因为抽象属性可能没有初始化器(initializer):
相反,你只能为这个属性指定一个类型:
下一步计划
为了帮助你的团队制定计划来尝试 TypeScript 4.4,你可以阅读 4.4 迭代计划。我们目前的目标是在 8 月中旬发布一个候选版本,并在 2021 年 8 月底发布稳定版本。从现在起到我们的候选版本发布时,我们的团队将努力解决各种已知问题并听取大家的反馈,因此请下载我们新发布的测试版,告诉我们你的想法!
迭代计划:https://github.com/microsoft/TypeScript/issues/44237
编程快乐!
——Daniel Rosenwasser 和 TypeScript 团队
原文链接
https://devblogs.microsoft.com/typescript/announcing-typescript-4-4-beta/
评论 2 条评论