动态调用方法装饰器时能否获取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
相关产品推荐
相关产品推荐

