如何从方法装饰器访问 class 元数据

How to access class metadata from method decorator

我有两个装饰器。一个 class 装饰器和一个方法装饰器。 class 装饰器定义了我想在方法装饰器中访问的元数据。

Class装饰者:

function ClassDecorator(topic?: string): ClassDecorator {
    return (target) => {
        Reflect.defineMetadata('topic', topic, target);
        // I've also tried target.prototype instead of target
        return target;
    };
}

方法装饰器:

interface methodDecoratorOptions {
    cmd: string
}

function MethodDecorator(options: decoratorOptions) {
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
        // HERE IS MY PROBLEM
        console.log('metaData is: ', Reflect.getMetadata('topic', target));
    }
}

这是我的 Class 定义:

@ClassDecorator('auth')
export class LoginClass {

    @MethodDecorator({
        cmd: 'login'
    })
    myMethod() {
        console.log('METHOD CALLED');
    }
}

问题:

MethodDecorator 的下面一行 returns metaData is: undefined。为什么未定义?

console.log('metaData is: ', Reflect.getMetadata('topic', target));

问题:

如何从 MethodDecorator 访问由 ClassDecorator 定义的元数据?

问题在于装饰器的执行顺序。方法装饰器先执行, class 装饰器在后执行。如果您考虑一下,这是有道理的,class 装饰器需要完整的 class 来执行,而创建 class 涉及创建方法并首先调用它们的装饰器。

一个简单的解决方法是让方法装饰器注册一个回调,然后在设置主题后由 class 装饰器调用:

function ClassDecorator(topic?: string): ClassDecorator {
    return (target) => {
        Reflect.defineMetadata('topic', topic, target.prototype);
        let topicFns: Array<() => void> = Reflect.getMetadata("topicCallbacks", target.prototype);
        if (topicFns) {
            topicFns.forEach(fn => fn());
        }
        return target;
    };
}

interface methodDecoratorOptions {
    cmd: string
}

function MethodDecorator(options: methodDecoratorOptions) {
    return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
        let topicFns: Array<() => void> = Reflect.getMetadata("topicCallbacks", target);
        if (!topicFns) {
            Reflect.defineMetadata("topicCallbacks", topicFns = [], target);
        }
        topicFns.push(() => {
            console.log('metaData is: ', Reflect.getMetadata('topic', target));
        });
    }
}

@ClassDecorator('auth')
class LoginClass {

    @MethodDecorator({
        cmd: 'login'
    })
    myMethod() {
        console.log('METHOD CALLED');
    }
}