实现 JS 装饰器来包装 class

Implementing JS decorator to wrap class

我正在尝试包装 class 构造函数并使用 class 装饰器注入一些逻辑。一切正常,直到我尝试扩展包装 class:扩展 class 原型中没有方法。

    function logClass(Class) {
      // save a reference to the original constructor
      const _class = Class;
    
      // proxy constructor
      const proxy = function(...args) {
        const obj = new _class(...args);
        // ... add logic here
        return obj
      }
    
      // copy prototype so intanceof operator still works
      proxy.prototype = _class.prototype;
    
      // return proxy constructor (will override original)
      return proxy;
    }
    
    @logClass
    class Base {
      prop = 5;
      test() {
        console.log("test")
      }
    }
    
    class Extended extends Base {
      test2() {
        console.log("test2")
      }
    }
    
    var base = new Base()
    base.test()
    var ext = new Extended()
    console.log(ext.prop)
    ext.test()
    ext.test2() // TypeError: ext.test2 is not a function

好的,所以我试图弄清楚您的代码有什么“错误”,但我无法让它工作,因为它没有进行类型检查。所以,作为最后的手段,我发布了我尝试的部分答案,它有效(有一些怪癖)所以我可以帮助其他更精通 TypeScript 的用户。

首先,怪癖:classTS中的装饰器不能修改类型的结构,所以如果你想,比如给装饰的class添加一个方法,你可以做到,但是在调用这些方法时你将不得不吃 up/suppress 不可避免的类型错误 (TS2339)。

在另一个问题中有解决此问题的方法:,但是如果您这样做,您将失去装饰器的当前干净语法

现在,我的解决方案或多或少直接取自 documentation:

function logClass<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    constructor(...args: any[]) {
      super(args);
      // ...add programmatic logic here
      //    (`super` is the decorated class, of type `T`, here)
    }

    // ...add properties and methods here
    log(message: string) {        // EXAMPLE
      console.log(`${super.constructor.name} says: ${message}`);
    }
  }
}
    
@logClass
class Base {
  prop = 5;
  test() {
    console.log("test");
  }

  constructor() {}
}

class Extended extends Base {
  test2() {
    console.log("test2");
  }
}

var base = new Base();
base.test();
var ext = new Extended();
console.log(ext.prop);
//base.log("Hello");  // unavoidable type error TS2339
ext.test();
ext.test2();