You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript 5中如何以编程方式应用装饰器?

TypeScript 5 中编程式应用装饰器的可行方案

在TypeScript 5中完全可以编程式应用装饰器,尽管新版装饰器引入了包含详细元信息的DecoratorContext参数增加了复杂度,但只要遵循ES标准装饰器的调用规范,就能实现批量应用装饰器的需求。

核心思路

TypeScript 5采用了符合ES标准的装饰器实现,装饰器函数现在接收三个核心参数:

  1. 装饰目标(比如类原型、方法函数、属性初始值)
  2. 装饰上下文(DecoratorContext,包含kind、name、access等元信息)
  3. 额外参数(部分装饰器类型会用到,比如参数装饰器的参数索引)

批量应用装饰器的本质是:按正确顺序调用每个装饰器,传递对应的参数,并处理装饰器的返回值(如果装饰器返回了新的目标,需要用它作为下一个装饰器的输入)。

实现批量装饰器的高阶函数

可以封装一个applyDecorators函数,接收多个装饰器并返回一个组合后的装饰器,自动处理调用顺序和参数传递:

示例代码

首先定义两个测试用的装饰器:

// 统计方法执行时间的装饰器
function time(target: any, context: ClassMethodDecoratorContext) {
  const methodName = context.name as string;
  return function(this: any, ...args: any[]) {
    console.time(methodName);
    const result = target.apply(this, args);
    console.timeEnd(methodName);
    return result;
  };
}

// 打印方法入参的装饰器
function logArgs(target: any, context: ClassMethodDecoratorContext) {
  return function(this: any, ...args: any[]) {
    console.log(`调用方法 ${String(context.name)},参数:`, args);
    return target.apply(this, args);
  };
}

然后实现applyDecorators组合函数:

type Decorator = (target: any, context: DecoratorContext, ...rest: any[]) => any;

function applyDecorators<T extends Decorator>(...decorators: T[]): T {
  return function(target: any, context: DecoratorContext, ...rest: any[]) {
    let currentTarget = target;
    // ES装饰器按从右到左顺序应用,因此需要倒序遍历装饰器数组
    for (const decorator of decorators.reverse()) {
      const decoratedResult = decorator(currentTarget, context, ...rest);
      // 如果装饰器返回了新的目标,更新为当前处理对象
      if (decoratedResult !== undefined) {
        currentTarget = decoratedResult;
      }
    }
    return currentTarget;
  } as T;
}

使用方式

直接在类成员上使用组合后的装饰器:

class MyClass {
  @applyDecorators(time, logArgs)
  method(a: number): void {
    console.log("MyClass.method()");
  }
}

// 调用测试
new MyClass().method(123);
// 输出顺序:
// 调用方法 method,参数: [123]
// MyClass.method()
// method: 0.xxms

手动编程式应用装饰器

如果需要更灵活的场景(比如在类定义后动态添加装饰器),可以手动构造DecoratorContext并调用装饰器:

class MyClass {
  method(a: number): void {
    console.log("MyClass.method()");
  }
}

// 构造方法装饰的上下文对象
const methodContext: ClassMethodDecoratorContext = {
  kind: "method",
  name: "method",
  static: false,
  private: false,
  access: {
    has: (obj) => "method" in obj,
    get: (obj) => obj.method
  },
  addInitializer: (init) => {
    // 若需要添加实例初始化逻辑,在此处处理(比如加入类的初始化队列)
  }
};

// 手动调用装饰器并替换原方法
const decoratedMethod = time(MyClass.prototype.method, methodContext);
if (decoratedMethod) {
  MyClass.prototype.method = decoratedMethod;
}

关键注意事项

  • 装饰器顺序:ES标准装饰器的应用顺序是从右到左(即代码中从上到下的反向),@applyDecorators(A, B)等价于@A @B,需通过倒序遍历保证正确执行顺序。
  • 返回值处理:部分装饰器(比如方法装饰器)会返回新的函数替代原目标,必须将这个返回值传递给下一个装饰器,否则装饰器效果会丢失。
  • 上下文完整性:手动构造DecoratorContext时,要确保access、kind等核心属性正确,否则装饰器可能无法正常工作。

内容的提问来源于stack exchange,提问作者Ivan Mushketyk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 21:15:33