Декоратор класса, как убедиться, что класс расширяет и реализует другие классы

Извините за странное название, я не совсем знаю, как описать то, что я пытаюсь сделать, в одном предложении.

Я должен определить кучу классов, которые будут расширяться от этого одного класса, а также реализовывать этот другой класс.

class SoulCoughing extends Super implements BonBon { /.../ }
class MoveAside extends Super implements BonBon { /.../ }
class LetTheManGoThru extends Super implements BonBon { /.../ }

Я написал своего рода функцию-оболочку, которую использую в качестве декоратора для этих классов.

const Eminem = function(klass: Constructable<????>) {
  const instance = new klass();
  // Do stuff
}

Constructable — это небольшой интерфейс, который я использую, потому что в противном случае TypeScript выдавал бы ошибку об отсутствии конструктора.

interface Constructable<T> {
  new(): T;
}

Теперь вот моя проблема, я не знаю, какой тип присвоить параметру klass в моей функции-оболочке? Я пытался сделать это:

... function(klass: Contrusctable<Super & BonBon>)

и это:

... function(klass: Contrusctable<Super | BonBon>)

Я также попытался изменить свой конструируемый интерфейс следующим образом:

interface Constructable<T, U> {
  new(): T & U;
}

... function(klass: Contrusctable<Super, BonBon>)

но я продолжаю получать ошибку Argument of type 'typeof SoulCoughing' is not assignable to parameter of type 'Constructable<everythingIveTriedSoFar>'.

Итак, мой вопрос: какое определение типа следует использовать с параметром klass? Я знаю, что могу просто использовать any, но мне бы очень хотелось убедиться, что передаваемый класс расширил Super и реализовал BonBon.


person snowfrogdev    schedule 11.12.2017    source источник


Ответы (1)


Я собираюсь предположить, что классы SoulCoughing и т. д. на самом деле не имеют конструкторов без аргументов и, следовательно, вообще не могут действовать как Constructable<{}>; наиболее вероятным виновником является то, что конструктор Super имеет обязательный аргумент, из-за которого все подклассы не будут соответствовать new() по умолчанию. Обратите внимание, что это также означает, что ваша реализация Eminem, вероятно, также хочет вызвать new klass(...) с некоторыми аргументами.

Правильный способ исправить это — объявить Constructable<T> конструктором с правильными типами аргументов. Допустим, Super выглядит так:

class Super {
  constructor(elevator: number, mezzanine: string) {
    //...
  }
}

Затем вы можете определить Constructable для соответствия:

interface Constructable<T extends Super & BonBon = Super & BonBon> {
  new(chump: number, change: string): T; // same args as Super
}

и Eminem нравится:

const Eminem = function(klass: Constructable) {
  const instance = new klass(2, "rise");
  // Do stuff
}

и наконец:

Eminem(SoulCoughing); // no error

Я оставил Constructable generic только на тот случай, если вы хотите, чтобы TypeScript сохранял тип конкретного подкласса, например:

const SlimShady = function <T extends Super & BonBon>(klass: Constructable<T>): T {
  return new klass(2, "fat");
}

// returns same type as passed-in constructor
const cutLean: MoveAside = SlimShady(MoveAside);

Хорошо, надеюсь, это поможет; удачи!

person jcalz    schedule 12.12.2017