javascript - JS TS bruk dekoratør på alle metoder/list opp klassemetoder


Jeg vil gjerne bruke en dekoratorfunksjon på alle metoder i en klasse slik at jeg kan erstatte:

class User {
    @log
    delete() {}

    @log
    create() {}

    @log
    update() {}
}

med

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

Svar #1

Lag en klasse-dekoratør og oppgi egenskapene på målobjektets prototype.

For hver eiendom:

  1. Hent egenskapsbeskrivelsen.
  2. Sørg for at det er for en metode.
  3. Pakk inn beskriververdien i en ny funksjon som logger informasjon om metodekallet.
  4. Definer den endrede egenskapsbeskrivelsen tilbake til egenskapen.

Det er viktig å endre eienskapsbeskrivelsen fordi du ønsker å sikre at dekoratøren din fungerer godt sammen med andre dekoratører som endrer eienskapsbeskrivelsen.

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

Metoder i basis klasse

Hvis du vil at dette også skal påvirke metodene i basisklassen, kan du ønske noe i denne retningen:

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

Svar #2

For den som snubler over dette i fremtiden:

Jeg tok inspirasjon fra Davids svar og lagde min egen versjon. Senere gjorde jeg det om til en npm-pakke: https://www.npmjs.com/package/decorate-all

I OPs scenario ville det blitt brukt slik

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

Relaterte artikler