Вывод общего типа с аргументом класса

У меня возникла проблема, которая определяет общий тип, основанный на типе, в котором я прошел.

У меня есть кусок кода, который активирует класс, я не могу получить информацию о типе из параметра типа, поэтому я передаю объект класса (а не экземпляр). Однако это нарушает вывод типа.

Вот упрощенный пример того, что я пытаюсь сделать:

interface IActivatable {
    id: number;
    name:string;
}

class ClassA implements IActivatable {
    public id: number;
    public name: string;
    public address:string;
}

class ClassB implements IActivatable {
    public id: number;
    public name: string;
    public age: number;
}

function activator<T extends IActivatable>(type:T): T {
    // do stuff to return new instance of T.
}

var classA:ClassA = activator(ClassA);

До сих пор единственным решением, которое я смог придумать, является изменение типа аргумента type на any и вручную также установить общий тип (как показано ниже). Однако это кажется длинным, но есть ли другой способ достичь этого.

function activator<T extends IActivatable>(type:any): T {
    // do stuff to return new instance of T.
}

var classA:ClassA = activator<ClassA>(ClassA);

Спасибо за любую помощь, которую вы можете дать.

Ответ 1

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

Итак, вот что вы можете сделать...

Вы можете создавать классы по имени, передавая имя в виде строки. Да; это включает в себя размахивание волшебной палочкой. Вам также нужно знать что-либо в вашей инструментальной цепочке, которая может повлиять на имена, например, любой minifier, который сокрушит имена (в результате чего ваша магическая строка будет не синхронизирована):

class InstanceLoader<T> {
    constructor(private context: Object) {

    }

    getInstance(name: string, ...args: any[]) : T {
        var instance = Object.create(this.context[name].prototype);
        instance.constructor.apply(instance, args);
        return <T> instance;
    }
}

var loader = new InstanceLoader<IActivatable>(window);

var example = loader.getInstance('ClassA');

Вы также можете получить имена типов из экземпляров во время выполнения, которые я показал в примере, приведенном ниже, из Получение TypeScript Имена классов во время выполнения:

class Describer {
    static getName(inputClass) { 
        var funcNameRegex = /function (.{1,})\(/;
        var results = (funcNameRegex).exec((<any> inputClass).constructor.toString());
        return (results && results.length > 1) ? results[1] : "";
    }
}

class Example {
}

class AnotherClass extends Example {
}

var x = new Example();
var y = new AnotherClass();

alert(Describer.getName(x)); // Example
alert(Describer.getName(y)); // AnotherClass

Это было бы актуально только в том случае, если вы хотите создать "другой такой же вид", как вы могли бы захватить имя типа, а затем использовать объект для создания другого.

Ответ 2

В соответствии со спецификацией языка вам нужно обратиться к типу класса с помощью функции-конструктора. Поэтому вместо type:T используйте type: { new(): T;} следующим образом:

function activator<T extends IActivatable>(type: { new(): T ;} ): T {
    // do stuff to return new instance of T.
    return new type();
}

var classA: ClassA = activator(ClassA);

Ответ 3

Вот как команда Typescript рекомендует это делать:
В основном то же самое, что и blorkfish, но извлекается в интерфейс.

interface IConstructor<T> {
    new (...args: any[]): T;

    // Or enforce default constructor
    // new (): T;
}

interface IActivatable {
    id: number;
    name: string;
}

class ClassA implements IActivatable {
    public id: number;
    public name: string;
    public address: string;
}

class ClassB implements IActivatable {
    public id: number;
    public name: string;
    public age: number;
}

function activator<T extends IActivatable>(type: IConstructor<T>): T {
    return new type();
}

const classA = activator(ClassA);

Источник

Ответ 4

Ваша проблема в том, что ClassA на самом деле не имеет типа T extends IActivatable. Фактически, ClassA в скомпилированном javascript фактически является функцией, которая возвращает функцию-конструктор.