chengaofeng
发布于 2024-07-15 / 10 阅读
0
0

Typescript5.5新特性

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] 形式的表达式中进行类型缩小,当 objkey 都是实际上的常量时。

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 类型的新提案方法,包括 unionintersectiondifferencesymmetricDifferenceisSubsetOfisSupersetOfisDisjointFrom

  • 特点: 这些方法不会改变原始的 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 也会跟踪符号链接目录,提高了在某些构建场景下的可靠性。

项目引用对自动导入的贡献

  • 自动导入功能现在可以跨项目引用工作,无需显式导入依赖项目。

性能和大小优化

  • 对象和控制流节点的单态化,以及控制流图的优化,提高了编译速度和编辑器体验。

  • 通过避免完整类型检查,transpileModuletranspileDeclaration 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 引入了几项重要的变化和新特性,包括:

  1. 新增 transpileDeclaration API:与 transpileModule 类似,用于生成单个声明文件。它不访问完整程序,因此只有在输入代码在新的 isolatedDeclarations 选项下没有错误时,才能生成准确的声明文件。

  2. 弃用特性:TypeScript 5.0 中弃用的一些选项和行为在 5.5 版本中不再有效,例如 charsettarget: ES3importsNotUsedAsValues 等。虽然可以在 tsconfig 中指定这些选项,但在 TypeScript 6.0 中将会报错。

  3. lib.d.ts 的变化:DOM 类型的更新可能会影响代码库的类型检查。

  4. 装饰器的更严格解析:TypeScript 对装饰器的语法要求更加严格,可能需要对现有装饰器进行修改以避免错误。

  5. undefined 不再是一个可定义的类型名称:修复了一个 bug,现在将 undefined 作为类型别名定义将会报错。

  6. 简化的引用指令声明发射:TypeScript 不再合成引用指令,用户编写的引用指令除非标注了 preserve="true" 属性,否则也不会被保留。这一变化旨在简化声明文件的生成,提高性能,并使库作者和使用者对声明文件有更好的控制。


评论