Вывод общего типа с аргументом класса
У меня возникла проблема, которая определяет общий тип, основанный на типе, в котором я прошел.
У меня есть кусок кода, который активирует класс, я не могу получить информацию о типе из параметра типа, поэтому я передаю объект класса (а не экземпляр). Однако это нарушает вывод типа.
Вот упрощенный пример того, что я пытаюсь сделать:
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 фактически является функцией, которая возвращает функцию-конструктор.