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

动态调用方法装饰器时能否获取NestJS反射元数据?

问题描述

在学习NestJS时,定义了如下方法装饰器MyDecorator,在AppService的test1方法上直接使用装饰器能正常获取design:*类型的反射元数据,但在test1内部动态调用装饰器处理test2方法时,无法获取到test2的design:*元数据,输出为undefined。

装饰器代码:

// my.decorator.ts
export function MyDecorator(): MethodDecorator {
  return (
    target: object,
    key: string | symbol,
    descriptor: PropertyDescriptor
  ) => {
    // 输出反射元数据
    console.log(target, key);
    console.log(Reflect.getMetadata('design:type', target, key));
    console.log(Reflect.getMetadata('design:paramtypes', target, key));

    SetMetadata(MY_DECORATOR_METADATA, true)(target, key, descriptor);
  };
}

服务类代码:

// app.service.ts
export class AppService {
  // 在test1上直接使用装饰器
  @MyDecorator()
  async test1(p1?: string) {
    // 动态在test2上调用装饰器
    MyDecorator()(AppService.prototype, 'test2', Object.getOwnPropertyDescriptor(AppService.prototype, 'test2'));

    return new Promise<string>((resolve) => setTimeout(() => resolve('test1'), 0))
  }

  async test2(p2?: string) {
    return new Promise<string>((resolve) => setTimeout(() => resolve('test2'), 0))
  }
}
原因与解决方法

原因

TypeScript的design:*系列元数据是编译阶段由TypeScript编译器自动注入的,只有当装饰器在编译阶段被显式应用到类或方法上时,编译器才会分析方法的类型信息并生成对应的元数据。动态调用装饰器是在运行时执行,此时编译已完成,编译器没有机会为test2生成design:*元数据,因此获取结果为undefined。

解决方法

1. 显式添加装饰器触发元数据生成

给test2方法添加任意装饰器(哪怕是空装饰器),让TypeScript在编译阶段为其注入元数据。示例:

// 定义空装饰器,仅用于触发元数据生成
export function TriggerMetadata(): MethodDecorator {
  return () => {};
}

export class AppService {
  @MyDecorator()
  async test1(p1?: string) {
    MyDecorator()(AppService.prototype, 'test2', Object.getOwnPropertyDescriptor(AppService.prototype, 'test2'));
    return new Promise<string>((resolve) => setTimeout(() => resolve('test1'), 0))
  }

  @TriggerMetadata() // 编译阶段应用,触发元数据注入
  async test2(p2?: string) {
    return new Promise<string>((resolve) => setTimeout(() => resolve('test2'), 0))
  }
}

如果允许MyDecorator的逻辑提前执行,也可以直接给test2添加@MyDecorator(),效果一致。

2. 手动定义元数据

如果不想在编译阶段给test2加装饰器,可以在运行时手动通过Reflect.defineMetadata添加所需的元数据。示例:

async test1(p1?: string) {
  // 手动添加test2的元数据
  Reflect.defineMetadata('design:type', Function, AppService.prototype, 'test2');
  Reflect.defineMetadata('design:paramtypes', [String], AppService.prototype, 'test2');
  Reflect.defineMetadata('design:returntype', Promise, AppService.prototype, 'test2');

  MyDecorator()(AppService.prototype, 'test2', Object.getOwnPropertyDescriptor(AppService.prototype, 'test2'));
  return new Promise<string>((resolve) => setTimeout(() => resolve('test1'), 0))
}

注意:手动添加需要自己准确指定类型信息,不如编译阶段自动注入可靠。

3. 确保tsconfig配置正确

确认tsconfig.json中已开启emitDecoratorMetadata和experimentalDecorators,这是TypeScript生成装饰器元数据的前提:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:00:34