Featured image of post 如何在 TypeScript 中使用标准装饰器Featured image of post 如何在 TypeScript 中使用标准装饰器

如何在 TypeScript 中使用标准装饰器

了解如何在 TypeScript 5.0 中编写和配置 ECMAScript 标准装饰器,而不依赖旧的实验标志。

介绍

在 TypeScript 中,装饰器长期以来一直是一个关键功能,被 Angular、NestJS 和 TypeORM 等主要框架所普及,用于管理元数据和依赖项注入。

然而,多年来,装饰器仍然是一个实验性功能(需要 experimentalDecorators 标志),依赖于与不断发展的 ECMAScript (JavaScript) 标准不同的遗留提案。

随着 TypeScript 5.0 的发布,引入了对标准 ECMAScript 装饰器(第 3/4 阶段)的官方支持。这为装饰器语法带来了改进的类型安全性和运行时一致性。本文介绍了如何在现代 TypeScript 中编写标准装饰器。


1. 实验装饰器与标准装饰器

主要区别在于装饰器函数的配置要求和运行时 API 签名。

配置变更

  • 遗留(实验): tsconfig.json 内需要 "experimentalDecorators": true
  • 标准(新): 默认情况下开箱即用。如果编译目标支持最新的 ECMAScript 功能,则标准装饰器会在本机进行转换。

上下文对象和类型安全

遗留装饰器可以访问松散的属性描述符,使得类型安全难以执行。标准装饰器接收结构化 context 对象 作为其第二个参数,提供有关装饰目标的类型安全元数据(例如其名称、可见性和目标类型)。


2. 编写标准方法装饰器

让我们编写一个标准的日志装饰器,每当执行类方法时,它都会将消息打印到控制台。

装饰器定义

// Standard method decorator signature
function logMethod<This, Args extends any[], Return>(
  target: (this: This, ...args: Args) => Return,
  context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
) {
  const methodName = String(context.name);

  // Return a wrapped replacement function
  return function (this: This, ...args: Args): Return {
    console.log(`[LOG] Calling ${methodName} with arguments:`, args);
    const result = target.apply(this, args);
    console.log(`[LOG] Finished ${methodName}. Returned:`, result);
    return result;
  };
}

应用装饰器

class UserService {
  @logMethod
  getUser(id: number) {
    return { id, name: "Takao" };
  }
}

const service = new UserService();
service.getUser(42); // Prints log messages during execution
  • ClassMethodDecoratorContext: 提供对目标元数据的访问,例如方法是否是静态的、私有的或者它所拥有的名称 (context.name),从而确保编译时的类型安全。

3. 关键迁移注意事项

虽然标准装饰器提高了类型安全性,但迁移遗留代码库需要谨慎:

  1. 反映元数据互操作性: 传统装饰器通常依赖 reflect-metadata 库来运行时注入依赖项(例如,在 NestJS 中)。标准装饰器提案实现了不同的元数据模型。在禁用实验标志之前验证框架兼容性。
  2. 弃用参数装饰器: 直接应用于方法参数的装饰器(例如 updateUser(@Inject() id: number))被排除在最终的 ECMAScript Stage 3 标准之外。标准装饰器只能针对类、方法、字段和访问器。

结论

TypeScript 5.0 中装饰器的标准化使该语言与 JavaScript 的未来保持一致。

  1. 原生使用装饰器,无需配置 experimentalDecorators 标志。
  2. 利用 context 参数安全地查询目标元数据。
  3. 重构遗留代码库时,请注意参数装饰器的弃用。

采用标准装饰器有助于确保元数据驱动的 TypeScript 代码与不断发展的 JavaScript 标准保持兼容。