【问题标题】:How to access class metadata from method decorator如何从方法装饰器访问类元数据
【发布时间】:2018-06-28 18:52:33
【问题描述】:

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

类装饰器:

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));
    }
}

这是我的类定义:

@ClassDecorator('auth')
export class LoginClass {

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

问题:

MethodDecorator 的以下行返回metaData is: undefined。为什么未定义?

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

问题:

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

【问题讨论】:

    标签: node.js typescript reflect-metadata typescript-decorator


    【解决方案1】:

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

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

    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');
        }
    }
    

    【讨论】:

    • 但老实说,我不喜欢这种解决方法。对我来说似乎不太可读。希望能找到更好的解决方案。
    • 我不知道另一种解决方法,它确实使代码更难阅读.. 但是装饰器再次成为您的基础架构的一部分,您将编写一次并使用多次,以及使用的装饰器没有改变,我认为这是更重要的部分
    • 您可以取消标记答案并等待更好的解决方法,我不介意:)。但是我已经看到并使用了类似的解决方法来解决这个问题,但还没有找到更好的解决方法,只是这个主题的变体(例如使用静态类字段而不是 Reflect` api)
    • 我不是故意冒犯你的。我非常感谢我现在确实理解了这个问题。我不明白为什么开发人员选择了装饰器执行的顺序。也许他们有这样做的原因,但对我来说这很不方便。
    • @tmuecksch 没有冒犯,真的:)。我确信某处有一个标准可以强制执行此操作。而且我相信他们考虑了另一种方式,但是对于大多数用例来说,这种顺序更有意义,不幸的是不适合你
    猜你喜欢
    • 1970-01-01
    • 2020-12-04
    • 2011-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-26
    • 1970-01-01
    • 2014-01-14
    相关资源
    最近更新 更多