programing

JTS는 모든 메소드에 데코레이터 적용 / 클래스 메소드 열거

megabox 2023. 7. 3. 22:39
반응형

JTS는 모든 메소드에 데코레이터 적용 / 클래스 메소드 열거

클래스 내의 모든 메소드에 데코레이터 기능을 적용하여 다음을 대체하고 싶습니다.

class User {
    @log
    delete() {}

    @log
    create() {}

    @log
    update() {}
}

와 함께

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

클래스 장식자를 만들고 대상의 프로토타입에 속성을 열거합니다.

각 속성에 대해:

  1. 속성 설명자를 가져옵니다.
  2. 방법을 위한 것인지 확인합니다.
  3. 메서드 호출에 대한 정보를 기록하는 새 함수로 설명자 값을 래핑합니다.
  4. 수정된 속성 설명자를 다시 속성으로 재정의합니다.

특성 설명자를 수정하는 다른 장식자와 함께 사용할 수 있도록 하려면 특성 설명자를 수정하는 것이 중요합니다.

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

앞으로 이 문제를 해결하지 못할 경우:

저는 데이비드의 대답에 영감을 받아 저만의 버전을 만들었습니다.저는 나중에 그것을 npm 패키지로 만들었습니다: https://www.npmjs.com/package/decorate-all

OP의 시나리오에서, 이것은 다음과 같이 사용될 것입니다.

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

추가 종속성을 끌어내고 싶지 않다면, 여기 @Papoch 구현의 단순화된 버전이 있습니다.

function DecorateAll(decorator: MethodDecorator) {
    return (target: any) => {
        const descriptors = Object.getOwnPropertyDescriptors(target.prototype);
        for (const [propName, descriptor] of Object.entries(descriptors)) {
            const isMethod =
                typeof descriptor.value == "function" &&
                propName != "constructor";
            if (!isMethod) {
                continue;
            }
            decorator(target, propName, descriptor);
            Object.defineProperty(target.prototype, propName, descriptor);
        }
    };
}

function Throttle(
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
) {
    const original = descriptor.value;
    descriptor.value = function () {
        console.log("throttle");
        return original.call(this);
    };
}

@DecorateAll(Throttle)
class SharedApiClient {
    async fetch1() {    }
    async fetch2() {    }
}

언급URL : https://stackoverflow.com/questions/47621364/js-ts-apply-decorator-to-all-methods-enumerate-class-methods

반응형