介绍
在 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. 关键迁移注意事项
虽然标准装饰器提高了类型安全性,但迁移遗留代码库需要谨慎:
- 反映元数据互操作性:
传统装饰器通常依赖
reflect-metadata库来运行时注入依赖项(例如,在 NestJS 中)。标准装饰器提案实现了不同的元数据模型。在禁用实验标志之前验证框架兼容性。 - 弃用参数装饰器:
直接应用于方法参数的装饰器(例如
updateUser(@Inject() id: number))被排除在最终的 ECMAScript Stage 3 标准之外。标准装饰器只能针对类、方法、字段和访问器。
结论
TypeScript 5.0 中装饰器的标准化使该语言与 JavaScript 的未来保持一致。
- 原生使用装饰器,无需配置
experimentalDecorators标志。 - 利用
context参数安全地查询目标元数据。 - 重构遗留代码库时,请注意参数装饰器的弃用。
采用标准装饰器有助于确保元数据驱动的 TypeScript 代码与不断发展的 JavaScript 标准保持兼容。

