Объявление типа Typescript для реализаций абстрактного класса - PullRequest
1 голос
/ 11 апреля 2020

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

abstract class AbstractFoo {
  abstract foo()
}

class Concrete1 extends AbstractFoo {
  foo() { ... }
}

class Concrete1 extends AbstractFoo {
  foo() { ... }
}

Теперь я пытаюсь создать карту, которая переходит от string к одному из конкретных классов. Обратите внимание, что я не пытаюсь отобразить экземпляры конкретных классов. Смотрите следующее:

const myMap: Map<string, typeINeedHelpWith> = new Map()
myMap.set('concrete1string', Concrete1)
myMap.set('concrete2string', Concrete2)

const instantiatedConcrete1 = new myMap.get('concrete1string')(...)

Есть ли определение типа для typeINeedHelpWith, которое позволило бы мне выполнить sh this?

Ответы [ 3 ]

1 голос
/ 11 апреля 2020

Используйте функцию, которая возвращает конкретный экземпляр в качестве значения карты.

Обновление : приведенное ниже предложение действительно только для deno

As предложение лучше использовать тип Record вместо Map, так как в противном случае неправильные ключи (mymap.get("concrete3")()) будут замечены как исключения времени выполнения.

abstract class AbstractFoo {
    abstract foo(): number;
  }

  class Concrete1 extends AbstractFoo {
    foo() {
      return 1;
    }
  }

  class Concrete2 extends AbstractFoo {
    foo() {
      return 2;
    }
  }

  const myMap: Record<string, () => AbstractFoo> = {
    "concrete1": () => new Concrete1(),
    "concrete2": () => new Concrete2(),
  };

  const instantiatedConcrete1 = myMap.concrete1();

  let fooResult = instantiatedConcrete1.foo();

Как отмечено в комментариях, обратите внимание, что я использовал фабричный шаблон для создания объектов.

Это признано хорошим шаблоном дизайна, но когда не требуется, может быть более подходящим решение на основе конструктора:

const myRec: Record<string, new() => AbstractFoo> = {
  "concrete1": Concrete1,
  "concrete2": Concrete2,
};

const iConcrete1 = new myRec.concrete1();

Синтаксис new() => AbstractFoo определяет сигнатуру конструктора, который не принимает аргументов и возвращает объект с формой AbstractFoo.

0 голосов
/ 11 апреля 2020

Вы можете попробовать что-то вроде этого, используя сопоставленные типы:

abstract class AbstractFoo {
    abstract foo(): string;
}

class Concrete1 extends AbstractFoo {
  foo() { return "Concrete 1" }
}

class Concrete2 extends AbstractFoo {
  foo() { return "Concrete 2" }
}

interface Mapping {
    'concrete1string': Concrete1,
    'concrete2string': Concrete2
}

class MyHeplerMap {
    maps: Mapping = {
        'concrete1string': new Concrete1(),
        'concrete2string': new Concrete2()
    };

    get<T extends keyof Mapping>(type: T): Mapping[T] {
        return this.maps[type];
    }
}

const map = new MyHeplerMap();
const c = map.get('concrete1string');
console.log(c.foo());

Пожалуйста, смотрите игровую площадку ссылка .

0 голосов
/ 11 апреля 2020

AFAIK единственный доступный тип - typeof AbstractFoo, то есть тип конструктора. Подобно typeof Array относится к типу конструктора Array, а тип Array относится к типу экземпляров массива.

Оба Concrete1 и Concrete2 расширяют этот тип (точнее, typeof Concrete1 и typeof Concrete2 расширяют typeof AbstractFoo)

Но вы не можете создать карту, как показано ниже:

const myMap: Map<string, typeof AbstractFoo> = new Map()
myMap.set('concrete1string', Concrete1)
myMap.set('concrete2string', Concrete2)

Нет, вы можете, но тип возврата myMap.get('concrete1string') - typeof AbstractFoo, а не typeof Concrete1. Вы не можете вызвать с new конструктор типа typeof AbstractFoo.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...