JS TS将装饰器应用于所有方法/枚举类方法

时间:2017-12-03 17:52:36

标签: javascript typescript decorator

我想将一个装饰器函数应用于类中的所有方法,以便我可以替换:

class User {
    @log
    delete() {}

    @log
    create() {}

    @log
    update() {}
}

@log
class User {
    delete() {}
    create() {}
    update() {}
}

2 个答案:

答案 0 :(得分:7)

创建一个类装饰器并枚举目标原型的属性。

对于每个属性:

  1. 获取属性描述符。
  2. 确保它是一种方法。
  3. 将描述符值包装在记录方法调用信息的新函数中。
  4. 将修改后的属性描述符重新定义回属性。
  5. 修改属性描述符非常重要,因为您要确保装饰器能够与修改属性描述符的其他装饰器一起使用。

    function log(target: Function) {
        for (const propertyName of Object.keys(target.prototype)) {
            const descriptor = Object.getOwnPropertyDescriptor(target.prototype, propertyName);
            const isMethod = descriptor.value instanceof Function;
            if (!isMethod)
                continue;
    
            const originalMethod = descriptor.value;
            descriptor.value = function (...args: any[]) {
                console.log("The method args are: " + JSON.stringify(args));
                const result = originalMethod.apply(this, args);
                console.log("The return value is: " + result);
                return result;
            };
    
            Object.defineProperty(target.prototype, propertyName, descriptor);        
        }
    }
    

    基类方法

    如果您希望这也影响基类方法,那么您可能需要这些内容:

    function log(target: Function) {
        for (const propertyName in target.prototype) {
            const propertyValue = target.prototype[propertyName];
            const isMethod = propertyValue instanceof Function;
            if (!isMethod)
                continue;
    
            const descriptor = getMethodDescriptor(propertyName);
            const originalMethod = descriptor.value;
            descriptor.value = function (...args: any[]) {
                console.log("The method args are: " + JSON.stringify(args));
                const result = originalMethod.apply(this, args);
                console.log("The return value is: " + result);
                return result;
            };
    
            Object.defineProperty(target.prototype, propertyName, descriptor);        
        }
    
        function getMethodDescriptor(propertyName: string): TypedPropertyDescriptor<any> {
            if (target.prototype.hasOwnProperty(propertyName))
                return Object.getOwnPropertyDescriptor(target.prototype, propertyName);
    
            // create a new property descriptor for the base class' method 
            return {
                configurable: true,
                enumerable: true,
                writable: true,
                value: target.prototype[propertyName]
            };
        }
    }
    

答案 1 :(得分:1)

对于将来偶然发现此问题的任何人:

我从大卫的回答中获得灵感并创建了我自己的版本。后来我把它做成了一个 npm 包:https://www.npmjs.com/package/decorate-all

在OP的场景中,它会像这样使用

@DecorateAll(log)
class User {
    delete() {}
    create() {}
    update() {}
}