TypeScript 5.0 新特性详解 - 装饰器与模块解析优化

2025-03-25 14:57 更新

装饰器

装饰器是 ECMAScript 的一项新功能,允许我们以可复用的方式自定义类及其成员。

考虑以下代码:

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}
const p = new Person("Ray");
p.greet();

greet 方法很简单,但假设它更复杂,比如包含异步逻辑、递归或副作用。无论具体实现如何,我们可能希望通过添加 console.log 调用来调试 greet

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    greet() {
        console.log("LOG: Entering method.");
        console.log(`Hello, my name is ${this.name}.`);
        console.log("LOG: Exiting method.");
    }
}

这种模式很常见。如果能有一种方法为每个方法添加这样的日志记录功能就好了。这就是装饰器的作用。

我们可以编写一个名为 loggedMethod 的函数,如下所示:

function loggedMethod(originalMethod: any, _context: any) {
    function replacementMethod(this: any, ...args: any[]) {
        console.log("LOG: Entering method.");
        const result = originalMethod.call(this, ...args);
        console.log("LOG: Exiting method.");
        return result;
    }
    return replacementMethod;
}

现在,我们可以使用 loggedMethod 来装饰 greet 方法:

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
    @loggedMethod
    greet() {
        console.log(`Hello, my name is ${this.name}.`);
    }
}
const p = new Person("Ray");
p.greet();
// 输出:
// LOG: Entering method.
// Hello, my name is Ray.
// LOG: Exiting method.

我们还可以利用上下文对象中的信息来打印被装饰方法的名称:

function loggedMethod(originalMethod: any, context: ClassMethodDecoratorContext) {
    const methodName = String(context.name);
    function replacementMethod(this: any, ...args: any[]) {
        console.log(`LOG: Entering method '${methodName}'.`);
        const result = originalMethod.call(this, ...args);
        console.log(`LOG: Exiting method '${methodName}'.`);
        return result;
    }
    return replacementMethod;
}

装饰器还可以用于属性、getter、setter 和自动访问器,甚至可以装饰类本身。


const 类型参数

当推断对象的类型时,TypeScript 通常会选择一个通用的类型。例如,names 的推断类型是 string[]

type HasNames = { names: readonly string[] };
function getNamesExactly<T extends HasNames>(arg: T): T["names"] {
    return arg.names;
}
// 推断类型:string[]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });

在 TypeScript 5.0 中,你可以在类型参数声明中添加 const 修饰符,使 const 类似的推断成为默认行为:

type HasNames = { names: readonly string[] };
function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
    return arg.names;
}
// 推断类型:readonly ["Alice", "Bob", "Eve"]
const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });


extends 中支持多个配置文件

当管理多个项目时,有一个基础配置文件供其他 tsconfig.json 文件扩展会很有帮助。TypeScript 5.0 现在允许 extends 字段接受多个条目。例如:

{
    "extends": ["./tsconfig1.json", "./tsconfig2.json"],
    "compilerOptions": {
        // ...
    }
}


所有枚举都是联合枚举

TypeScript 5.0 通过为每个计算成员创建唯一类型,使所有枚举成为联合枚举。这意味着所有枚举现在都可以被缩小范围,并且它们的成员也可以作为类型引用。


模块解析捆绑器

TypeScript 4.7 引入了 node16nodenext 选项,用于其 --module--moduleResolution 设置。TypeScript 现在引入了一种新的策略:--moduleResolution bundler。如果你使用的是现代捆绑器,如 Vite、esbuild、swc、Webpack、Parcel 等,新的 bundler 选项应该非常适合你。


解析自定义标志

允许导入 TS 扩展

--allowImportingTsExtensions 允许 TypeScript 文件使用 TypeScript 特定的扩展(如 .ts.mts.tsx)相互导入。

解析包 JSON 导出

--resolvePackageJsonExports 强制 TypeScript 在从 node_modules 中的包读取时,咨询 package.json 文件中的 exports 字段。

解析包 JSON 导入

--resolvePackageJsonImports 强制 TypeScript 在从文件开始以 # 的查找中咨询 package.json 文件中的 imports 字段。

允许任意扩展

--allowArbitraryExtensions 允许导入路径以未知的 JavaScript 或 TypeScript 文件扩展结束。

自定义条件

--customConditions 指定在 TypeScript 从 package.jsonexportsimports 字段解析时应成功的其他条件。


原样模块语法

TypeScript 5.0 引入了一个新选项 --verbatimModuleSyntax,使导入和导出的规则更加简单:任何没有 type 修饰符的导入或导出都会保留,而使用 type 修饰符的导入或导出将被完全删除。

支持 export type *

TypeScript 5.0 添加了对 export type * as ns from "module"export type * from "module" 的支持。


JSDoc 中的 @satisfies 支持

TypeScript 5.0 在 JSDoc 中支持新的 @satisfies 标签,确保表达式的类型兼容,而不影响其自身类型。


JSDoc 中的 @overload 支持

TypeScript 5.0 允许在 JSDoc 中使用 @overload 标签声明函数的重载。


--build 下传递特定于 emit 的标志

TypeScript 现在允许在 --build 模式下传递以下标志:

  • --declaration
  • --emitDeclarationOnly
  • --declarationMap
  • --sourceMap
  • --inlineSourceMap


编辑器中的大小写不敏感导入排序

TypeScript 现在默认检测大小写敏感性,以解决在排序导入和导出时的不同解释问题。


穷尽的 switch/case 补全

在编写 switch 语句时,TypeScript 会检测被检查值是否具有字面量类型,并提供补全以涵盖每个未覆盖的 case


速度、内存和包大小优化

TypeScript 5.0 在代码结构、数据结构和算法实现方面进行了大量改进,使整个体验更快,安装包也更小。


破坏性更改和弃用

运行时要求

TypeScript 现在针对 ECMAScript 2018。对于 Node 用户,这意味着至少需要 Node.js 10 及更高版本。

API 破坏性更改

在 TypeScript 5.0 中,我们迁移到模块,删除了一些不必要的接口,并进行了一些正确性改进。

关系运算符中的隐式类型转换被禁止

在 5.0 中,这也将应用于关系运算符 ><<=>=

枚举大修

TypeScript 5.0 清理了一些长期存在的 enum 问题,并减少了理解各种 enum 类型所需的概念数量。

在构造函数中使用参数装饰器时更准确的类型检查

TypeScript 5.0 在 --experimentalDecorators 下对装饰器进行更准确的类型检查。

弃用和默认更改

在 TypeScript 5.0 中,我们弃用了以下设置和设置值:

  • --target: ES3
  • --out
  • --noImplicitUseStrict
  • --keyofStringsOnly
  • --suppressExcessPropertyErrors
  • --suppressImplicitAnyIndexErrors
  • --noStrictGenericChecks
  • --charset
  • --importsNotUsedAsValues
  • --preserveValueImports
  • 项目引用中的 prepend

这些配置将继续被允许,直到 TypeScript 5.5,届时它们将被完全删除。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号