Как динамически создавать экземпляры разных классов, с доступом к их типам?

В TypeScript можно динамически создавать экземпляры разных классов, используя концепцию фабрик (фабричных методов), а также с помощью функций-конструкторов и рефлексии.

1. Фабрики (фабричные методы):
Фабрики представляют собой функции, которые создают экземпляры классов. Они обычно определены в базовом абстрактном классе или интерфейсе, а каждый конкретный класс имеет свою собственную реализацию фабрики. Вот пример:

abstract class Animal {
  abstract makeSound(): void;
}

class Dog extends Animal {
  makeSound(): void {
    console.log('Woof!');
  }
}

class Cat extends Animal {
  makeSound(): void {
    console.log('Meow!');
  }
}

// Фабрика для создания экземпляров класса Animal
class AnimalFactory {
  createAnimal(type: string): Animal {
    if (type === 'dog') {
      return new Dog();
    } else if (type === 'cat') {
      return new Cat();
    } else {
      throw new Error('Invalid animal type');
    }
  }
}

// Использование фабрики
const animalFactory = new AnimalFactory();
const dog = animalFactory.createAnimal('dog');
const cat = animalFactory.createAnimal('cat');
dog.makeSound(); // Выведет 'Woof!'
cat.makeSound(); // Выведет 'Meow!'

2. Функции-конструкторы:
В TypeScript также можно использовать функции-конструкторы для создания экземпляров разных классов. Например:

class Person {
  constructor(public name: string) {}
}

class Car {
  constructor(public brand: string) {}
}

function createInstance<T>(ctor: new () => T): T {
  return new ctor();
}

const person = createInstance(Person);
const car = createInstance(Car);

console.log(person instanceof Person); // true
console.log(car instanceof Car); // true

3. Рефлексия:
TypeScript поддерживает рефлексию на этапе выполнения, что позволяет получать информацию о типах во время исполнения программы. Для создания экземпляров классов по типу можно использовать рефлекторы. Вот пример:

class Person {
  constructor(public name: string) {}
}

class Car {
  constructor(public brand: string) {}
}

function createInstance(type: any): any {
  return new type();
}

const person = createInstance(Person);
const car = createInstance(Car);

console.log(person instanceof Person); // true
console.log(car instanceof Car); // true

В этом примере функция createInstance принимает тип как аргумент и создает экземпляр соответствующего класса.

Надеюсь, эти примеры помогут вам понять, как динамически создавать экземпляры разных классов с доступом к их типам в TypeScript.