TypeScript 5.5 引入了类型谓词的自动推断,这是由 Dan Vanderkam 实现的。这项特性改进了 TypeScript 在控制流分析中对变量类型变化的跟踪能力,特别是在处理数组和过滤操作时。
类型谓词自动推断
在之前的 TypeScript 版本中,即使通过过滤操作移除了 undefined
值,数组中的元素类型仍然被认为可能是 undefined
。例如,尝试对一个可能包含 undefined
的鸟类数组进行操作会导致错误,因为 TypeScript 不能确认数组已经被过滤。
function makeBirdCalls(countries: string[]) {
const birds = countries
.map(country => nationalBirds.get(country))
.filter(bird => bird !== undefined);
for (const bird of birds) {
bird.sing(); // 在 TypeScript 5.5 之前,这里会报错
}
}
在 TypeScript 5.5 中,通过引入类型谓词的自动推断,上述代码现在可以正常工作,因为 TypeScript 能够理解 filter
操作已经移除了所有 undefined
值,从而将 birds
数组的类型精确为 Bird[]
而不是 (Bird | undefined)[]
。
类型谓词的工作原理
类型谓词形式为 x is T
,表示如果函数返回 true
,则参数 x
是类型 T
。这种机制允许 TypeScript 在类型检查时更准确地推断变量的类型。
使用条件
TypeScript 会在以下条件满足时自动推断函数返回类型谓词:
函数没有显式的返回类型或类型谓词注解。
函数只有一个返回语句,没有隐式返回。
函数不修改其参数。
函数返回一个与参数类型细化相关的布尔表达式。
示例
// 自动推断为类型谓词
const isNumber = (x: unknown) => typeof x === 'number';
const isNonNullish = <T,>(x: T) => x != null;
注意事项
类型谓词具有“当且仅当”语义:如果函数返回
true
,则参数具有类型T
;如果返回false
,则参数不具有类型T
。如果期望自动推断类型谓词但没有发生,可能是因为函数的返回条件不满足上述规则。
控制流缩小对常量索引访问
TypeScript 现在能够在 obj[key]
形式的表达式中进行类型缩小,当 obj
和 key
都是实际上的常量时。
function f1(obj: Record<string, unknown>, key: string) {
if (typeof obj[key] === "string") {
obj[key].toUpperCase(); // 现在是允许的
}
}
这项特性使得 TypeScript 在处理不变的对象和键时,能够更准确地推断出 obj[key]
的类型。
@import 标签
问题: 在 JavaScript 中,仅为类型检查导入类型时,不能直接导入运行时不存在的类型,否则会导致运行时错误。
解决方案: TypeScript 引入了新的
@import
JSDoc 注释标签,允许开发者在不影响运行时行为的情况下,仅为类型检查导入类型。示例:
/** @import { SomeType } from "some-module" */ /** * @param {SomeType} myValue */ function doSomething(myValue) { // ... }
正则表达式语法检查
改进: TypeScript 现在对正则表达式进行基本的语法检查,能够发现并指出常见的错误,如意外的闭合括号、不存在的反向引用等。
示例:
let myRegex = /@robot(\s+(please|immediately)))? do some task/; // error! Unexpected ')'.
ECMAScript Set 新方法
新增方法: TypeScript 5.5 声明了 ECMAScript Set 类型的新提案方法,包括
union
、intersection
、difference
、symmetricDifference
、isSubsetOf
、isSupersetOf
和isDisjointFrom
。特点: 这些方法不会改变原始的 Set 对象,而是返回一个新的 Set 对象或布尔值,表示操作的结果。
示例:
let fruits = new Set(["apples", "bananas", "pears", "oranges"]); console.log(fruits.union(new Set(["oranges"]))); // Set(4) {'apples', 'bananas', 'pears', 'oranges'}
这些特性提高了 TypeScript 的灵活性和表达能力,同时增强了对现代 JavaScript 开发的支持。
TypeScript的隔离声明和配置文件模板变量
隔离声明(Isolated Declarations)
- 背景: TypeScript的`.d.ts`文件描述了库和模块的结构,包括类型签名而不包括实现细节。这些文件让TypeScript能够高效地检查库的使用而无需分析库本身。
- 用例:
- 快速生成声明文件: 想象创建一个工具,以更快的速度生成声明文件,比如作为发布服务的一部分或新的打包工具。
- 并行声明生成和检查: 在包含多个项目的monorepo中,如果能够同时在不同核心上运行每个项目的类型检查,将大大提高效率。
- 解决方案: 明确类型!如果开发者愿意显式地写出他们导出的类型,工具就能在不需要查看模块实现的情况下生成声明文件。
- `--isolatedDeclarations`选项: 这个新选项在模块的导出没有足够注解时,让TypeScript报错。这意味着,如果你的文件导出没有被充分注释,TypeScript会报错。
配置文件模板变量 ${configDir}
- 背景: 在许多代码库中,常常通过`extends`字段在`tsconfig.json`文件中重用一个共享的`tsconfig.base.json`文件作为“基础”配置。
- 问题: 所有在`tsconfig.json`文件中的路径都是相对于该文件本身的位置。这意味着,如果有一个被多个项目使用的共享`tsconfig.base.json`文件,相对路径在派生项目中往往不是很有用。
- 解决方案: TypeScript 5.5引入了一个新的模板变量`${configDir}`。当在`tsconfig.json`或`jsconfig.json`文件的某些路径字段中写入`${configDir}`时,这个变量会被替换为给定编译中配置文件所在目录。这使得共享配置文件跨项目更容易,确保配置文件更具可移植性。
总结
TypeScript的隔离声明功能和`${configDir}`配置文件模板变量旨在提高开发效率和配置的可移植性。隔离声明通过要求显式类型注解来简化声明文件的生成,而`${configDir}`变量解决了共享配置文件中相对路径问题,使得配置更容易在不同项目间共享。
更多
声明文件生成的依赖项处理改进
TypeScript 5.5 在生成声明文件时,对于
package.json
中明确列出的依赖项,允许更宽松的引用导入,减少了类型推断错误。
编辑器和观察模式的可靠性提升
新增功能或修复现有逻辑,减少了需要重启 TSServer/编辑器的情况。
配置文件错误刷新改进
TypeScript 现在能够在修复
tsconfig.json
文件中的所有错误后,清除旧的错误提示。
删除后立即写入的处理改进
对于删除后立即创建新文件的操作,TypeScript 5.5 采取了更细致的处理方式,避免了完全重建项目的需要。
链接失败解析中的符号链接跟踪
即使模块解析失败,TypeScript 也会跟踪符号链接目录,提高了在某些构建场景下的可靠性。
项目引用对自动导入的贡献
自动导入功能现在可以跨项目引用工作,无需显式导入依赖项目。
性能和大小优化
对象和控制流节点的单态化,以及控制流图的优化,提高了编译速度和编辑器体验。
通过避免完整类型检查,
transpileModule
和transpileDeclaration
API 的构建时间显著加快。TypeScript 包大小从 30.2 MB 减少到 20.4 MB,打包大小从 5.5 MB 减少到 3.7 MB。
声明文件的节点重用
改进了声明文件生成时的类型注释重用,提高了性能并保留了用户意图。
缓存来自歧义联合的上下文类型
对象字面量的上下文类型计算现在被缓存,减少了编译时间。
ECMAScript 模块中更易于使用的 API
修复了在 ECMAScript 模块中使用 TypeScript 包时的命名导入问题。
transpileDeclaration
API
新增 API,专门用于生成单个 TypeScript 文件的声明文件,适用于不需要完整程序上下文的场景。
这些更新提高了 TypeScript 的性能、可靠性和易用性,同时减少了包大小,为开发者提供了更加流畅的编码体验。
TypeScript 5.5 引入了几项重要的变化和新特性,包括:
新增
transpileDeclaration
API:与transpileModule
类似,用于生成单个声明文件。它不访问完整程序,因此只有在输入代码在新的isolatedDeclarations
选项下没有错误时,才能生成准确的声明文件。弃用特性:TypeScript 5.0 中弃用的一些选项和行为在 5.5 版本中不再有效,例如
charset
、target: ES3
、importsNotUsedAsValues
等。虽然可以在tsconfig
中指定这些选项,但在 TypeScript 6.0 中将会报错。lib.d.ts
的变化:DOM 类型的更新可能会影响代码库的类型检查。装饰器的更严格解析:TypeScript 对装饰器的语法要求更加严格,可能需要对现有装饰器进行修改以避免错误。
undefined
不再是一个可定义的类型名称:修复了一个 bug,现在将undefined
作为类型别名定义将会报错。简化的引用指令声明发射:TypeScript 不再合成引用指令,用户编写的引用指令除非标注了
preserve="true"
属性,否则也不会被保留。这一变化旨在简化声明文件的生成,提高性能,并使库作者和使用者对声明文件有更好的控制。