Реализация декоратора JS для обертывания класса

Я пытаюсь обернуть конструктор класса и ввести некоторую логику с помощью декоратора класса. Все работало нормально, пока я не попытался расширить обернутый класс: у расширенного класса нет методов в прототипе.

    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

1 ответ

Решение

Итак, я попытался выяснить, что "не так" в вашем коде, но мне не удалось заставить его работать, потому что он не проверял тип. Итак, в крайнем случае, я публикую частичный ответ о своей попытке, который работает (с некоторыми причудами), поэтому я могу помочь другим пользователям, которые более сообразительны с TypeScript.

Прежде всего, причуды: декораторы классов в TS не могут изменять структуру типа, поэтому, если вы хотите, например, добавить метод в украшенный класс, вы сможете это сделать, но вам придется съесть / подавлять неизбежные ошибки типа (TS2339) при вызове этих методов.

В этом другом вопросе есть обходной путь: методы добавления Typescript с типом декоратора не существуют, но вы потеряете этот текущий чистый синтаксис для декораторов, если сделаете это.

Теперь мое решение, взятое более или менее прямо из документации:

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();
Другие вопросы по тегам