在实现为箭头功能的类方法上使用TypeScript Decorator

免责声明:我之前也曾在SO上发布过类似的问题。它们确实帮助我解决了一些TypeScript编译器问题。但是我找不到一个有效的,完整的代码示例。

问题:我想在某些类方法上使用TypeScript装饰器。但是问题是,如果我的方法实现为箭头函数,那么我将无法使用它们。我主要根据一些文章和SO帖子修改了我的代码(在运行时,它们被更多地视为类属性)

  1. 可选描述符descriptor?: TypedPropertyDescriptor<any>

  2. 返回为any

之后,编译器接受了装饰器,并且我的代码正在正确执行。但是我的装饰器代码无法正常工作。下面提供了简化的代码以供参考。使代码正常工作的任何帮助都将有所帮助。

装饰器

// Decorator declaration
export function LogPerf(
    message = '',) {
    return function (
        target: any,key: string | symbol,descriptor?: TypedPropertyDescriptor<any>,) {
        if (!descriptor) {
            console.log('Logging performance of property...'); // This log doesn't execute at runtime
            return;
        }

        // following log also doesn't work
        const original = descriptor.value;

        descriptor.value = function (...args: any[]) {
            const timeStartMilli: number = performance.now();

            const result = original.apply(this,args);

            const timeEndMilli: number = performance.now();

            console.log(`Call to ${key.toString()} took ${timeEndMilli - timeStartMilli} ms. Message: ${message}`);

            return result;
        };

        return descriptor as any;
    };
}

还有一个示例用法,它不起作用。

注意:在普通实例方法上使用时,此方法工作正常。

// Decorater use (on class method,implemented as arrow function)
public export Someclass {

    @LogPerf()
    public myMethod = (
        data: MyData,): Somehting => {
        // Do something with data
    }
}
z71468755z 回答:在实现为箭头功能的类方法上使用TypeScript Decorator

问题在于装饰器旨在应用于prototype对象。如果您检查了转译的js,就会明白为什么它不起作用。

class SomeClass {
    @LogPerf()
    public myMethod = (
        data: MyData,): Somehting => {
        // Do something with data
    }
}

/** transpiled to: */

class SomeClass {
    constructor() {
        this.myMethod = (data) => {
            // Do something with data
        };
    }
}

__decorate([
    LogPerf()
],SomeClass.prototype,"myMethod",void 0);

请参见,修饰符应用于SomeClass.prototype["myMethod"]。但是,当您将方法定义为箭头函数时,它将在构造函数中初始化并分配给this对象。

解决方法是:

  1. 将方法定义为prototype对象上的普通函数,然后在初始化时绑定this
  2. 重载LogPerf()签名以接受替代用法,如下所示:
class SomeClass {
    public myMethod = LogPerf()((
        data: MyData,): Somehting => {
        // Do something with data
    })
}

如果愿意,可以将方法1集成到@LogPerf装饰器中。您可以从autobind-decorator库中引用这段代码。

function boundMethod(target,key,descriptor) {
  var fn = descriptor.value;

  if (typeof fn !== 'function') {
    throw new TypeError("@boundMethod decorator can only be applied to methods not: ".concat(_typeof(fn)));
  } // In IE11 calling Object.defineProperty has a side-effect of evaluating the
  // getter for the property which is being replaced. This causes infinite
  // recursion and an "Out of stack space" error.


  var definingProperty = false;
  return {
    configurable: true,get: function get() {
      // eslint-disable-next-line no-prototype-builtins
      if (definingProperty || this === target.prototype || this.hasOwnProperty(key) || typeof fn !== 'function') {
        return fn;
      }

      var boundFn = fn.bind(this);
      definingProperty = true;
      Object.defineProperty(this,{
        configurable: true,get: function get() {
          return boundFn;
        },set: function set(value) {
          fn = value;
          delete this[key];
        }
      });
      definingProperty = false;
      return boundFn;
    },set: function set(value) {
      fn = value;
    }
  };
}

方法2的实现部分很简单,您只需要处理仅传入一个函数arg的情况,我相信您可以弄清楚。

棘手的部分是如何满足TypeScript的要求,这是您需要的函数签名:

export function LogPerf(message?: string): {
    <T extends Function>(fn: T): T;
    (target: Object,key: string | symbol,descriptor?: TypedPropertyDescriptor<any> | undefined): any;
}
export function LogPerf(message = '') {

  // YOUR IMPLEMENTATION GOES HERE

}

Typescript Playground

本文链接:https://www.f2er.com/2377325.html

大家都在问