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

Posted

技术标签:

【中文标题】打字稿装饰器不能使用箭头函数【英文标题】:Typescript decorators not working with arrow functions 【发布时间】:2018-09-30 09:08:54 【问题描述】:

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

例如

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 中正确设置了所有参数。我的整个项目都支持“ES6”目标,我希望装饰器支持箭头功能。

【问题讨论】:

【参考方案1】:

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

handleMessage = message =&gt; ... 类字段的情况下没有descriptor,因为它在类原型中不存在。类字段只是在构造函数中分配给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 中所解释的,原型方法可以优于实例方法,原因有几个。原因之一是它们可以无缝装饰,因为装饰器应用于类原型。箭头方法唯一真正的好处是它自然绑定到类实例,但是由于装饰器已经在使用中,如果需要,原型方法可以绑定在装饰器中(这就是universalMethodDecorator 基本上所做的;它是箭头的 noop方法)。

【讨论】:

handleMessage 方法确实存在于一个类中。我刚刚编辑了问题并添加了此信息。 我不确定你的意思。当 handleMessage 是原型方法时它有效,当它不是原型方法时它不起作用,不是吗?这就是答案的假设,handleMessage 属性没有描述符,因为它在类中不存在 prototype 我尝试在这里使用universalMethodDecorator,但仍然得到同样的错误。要点:我的装饰器将始终具有更高阶的功能,因为我也想打印从装饰器发送的消息。例如:@performaceLog('validate', 'message') handleMessage = msg=> console.log(msg);返回真; ;应该打印 'validate'、'message' 以及实际的函数参数 msg。所以我总是用高阶函数来包装我的装饰器函数,比如: 我尝试使用universalMethodDecorator,但仍然得到同样的错误。要点:我的装饰器将始终具有更高阶的功能,因为我也想打印从装饰器发送的消息。例如:@performaceLog('validate', 'message') handleMessage = msg=> console.log(msg);返回真; ;应该打印 'validate'、'message' 以及实际的函数参数 msg。我将始终用高阶 func 包装我的装饰器: function performaceLog(...args: any[]) function universalMethodDecorator(target, prop, descriptor) ..... 如果你做的一切都正确,你不应该得到这个错误。您能否提供一种通过当前尝试(stackblitz,plunk)复制此错误的方法?它是否是装饰器工厂并不重要,它只是成为高阶函数并且行为方式相同。

以上是关于打字稿装饰器不能使用箭头函数的主要内容,如果未能解决你的问题,请参考以下文章

带有箭头功能的打字稿装饰器

打字稿 - 在箭头(回调)函数中使用它 - 猫鼬

带有箭头函数的打字稿重载方法

在打字稿中应用箭头函数作为对象属性

打字稿重载箭头功能不起作用

类装饰器上的打字稿文档-返回“类扩展构造函数”的函数