打字稿装饰器无法使用箭头功能 [英] Typescript decorators not working with arrow functions

查看:96
本文介绍了打字稿装饰器无法使用箭头功能的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a typescript decorator factory which console logs total time taken to execute a function, actual function execution results and parameters passed to the decorator as well.

例如

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).

I am using the above decorator with a function which is present in a class: (considering my class has other methods as well like ctor, get, set and 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)时,会抛出错误:

But when I change the function handleMessage to support arrow format (ES6), it throws me an error:

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

错误消息:

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

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

I have all the parameters set correctly in my tsconfig.json. I have my entire project supporting "ES6" target and I want the decorator to support arrow functions.

推荐答案

performaceLog仅应用于原型方法,因为它依赖于descriptor,应该是可选的.

performaceLog is supposed to work with prototype methods only because it relies on descriptor, which should be optional.

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

There is no descriptor in case of handleMessage = message => ... class field because it doesn't exist on class prototype. Class fields are just assigned to this in constructor.

此行由于相同的原因而无法正常工作:

This line won't work for same reason:

descriptor = Object.getOwnPropertyDescriptor(target, key);

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

In order to patch arrow method in decorator, a trap should be set on class prototype. Here is an example of universal decorator that can be used with both prototype and instance methods; it uses get/set to catch proper this context and cache decorated function to patchFn variable. It returns a descriptor regardless of descriptor parameter:

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 旧版装饰器的行为可能有所不同.

This applies only to TypeScript decorators. Babel legacy decorators may behave differently.

此答案中所述,由于多种原因,原型方法可能比实例方法更可取.原因之一是由于可以将装饰器应用于类原型,因此可以进行无缝装饰. arrow方法的唯一真正好处是它自然地绑定到了类实例,但是由于装饰器已经在使用中,因此如果需要,可以在装饰器中绑定原型方法(这是universalMethodDecorator的基本作用;对arrow而言这是无用的)方法).

As explained in this answer, prototype methods can be preferred to instance methods for several reasons. One of the reasons is that they can be seamlessly decorated because decorators are applied to class prototype. The only real benefit of arrow method is that it is naturally bound to class instance, but since the decorator is already in use, prototype method can be bound in decorator if needed (this is what universalMethodDecorator basically does; it is noop for arrow methods).

这篇关于打字稿装饰器无法使用箭头功能的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆