打字稿装饰器不使用箭头功能

时间:2018-04-19 18:53:03

标签: javascript typescript ecmascript-6 decorator typescript-decorator

我有一个打字稿装饰工厂,控制台记录执行函数所花费的总时间,实际函数执行结果以及传递给装饰器的参数。

e.g。

export function performaceLog(...args: any[]) {
return (target: Object, key: string, descriptor: TypedPropertyDescriptor<any>) => {
    var msg = '';
    if (args.length != 0) {
        msg = args.map(arg => arg).join(' ');
    }

    if (descriptor === undefined) {
        descriptor = Object.getOwnPropertyDescriptor(target, key);
    }

    if (typeof descriptor.value !== 'function') {
        throw new SyntaxError('Only functions can be used with log decorators');
    }

    var originalMethod = descriptor.value.bind(target);

    descriptor.value = function() {
        var funcArgs: any = [];
        for (var i = 0; i < arguments.length; i++) {
            funcArgs[i - 0] = arguments[i];
        }
        var startTime = performance.now();
        console.log(`Begin function ${key} with params (${funcArgs}) : ${msg}`);
        var result = originalMethod.apply(this, funcArgs);
        var endTime = performance.now();
        console.log(`End function ${key}. Execution time: ${endTime - startTime} milliseconds. Return result : ${result}`);
        return result;
    };
    return descriptor;
};
}

我正在使用上面的装饰器,其中包含一个类中的函数: (考虑到我的班级还有其他方法,如ctor,get,set和utils)。

class LoggerService {
    @performaceLog('validate', 'message')
    handleMessage(message) {
        console.log(message);
        return true;
    };
}

函数调用如下所示:

handleMessage('decoratorValidation');

这给了我完美的输出:

Begin function handleMessage with params (decoratorValidation) : validate message     
decoratorValidation
End function handleMessage. Execution time: 0.3000000142492354 milliseconds. 
Return result : true

但是当我将函数handleMessage更改为支持箭头格式(ES6)时,它会抛出一个错误:

@performaceLog('validate', 'message')
handleMessage = message => {
    console.log(message);
    return true;
};

错误讯息:

Unable to resolve signature of property decorator when called as an expression.

我在tsconfig.json中正确设置了所有参数。我的整个项目都支持&#34; ES6&#34;目标,我希望装饰器支持箭头功能。

1 个答案:

答案 0 :(得分:4)

performaceLog应该只使用原型方法,因为它依赖descriptor,这应该是可选的。

descriptor类字段没有handleMessage = message => ...,因为它在类原型上不存在。类字段仅在构造函数中分配给this

此行不会出于同样的原因:

descriptor = Object.getOwnPropertyDescriptor(target, key);

为了在装饰器中修补箭头方法,应该在类原型上设置陷阱。以下是通用装饰器的示例,可以与原型和实例方法一起使用;它使用get / set来捕获正确的this上下文并将装饰函数缓存到patchFn变量。无论descriptor参数如何,它都会返回描述符:

function universalMethodDecorator(target: any, prop: string, descriptor?: TypedPropertyDescriptor<any>): any {
    let fn;
    let patchedFn;

    if (descriptor) {
        fn = descriptor.value;
    }

    return {
        configurable: true,
        enumerable: false,
        get() {
            if (!patchedFn) {
                patchedFn = (...args) => fn.call(this, ...args);
            }
            return patchedFn; 
        },
        set(newFn) {
            patchedFn = undefined;
            fn = newFn;
        }
    };

}

这仅适用于TypeScript装饰器。 Babel legacy decorators可能表现不同。

this answer中所述,由于多种原因,原型方法可能比实例方法更受欢迎。其中一个原因是它们可以无缝装饰,因为装饰器应用于类原型。 arrow方法的唯一真正好处是它自然地绑定到类实例,但由于装饰器已经在使用中,如果需要,可以在装饰器中绑定原型方法(这是universalMethodDecorator基本上所做的;它是noop对于箭头方法)。