В TypeScript 2.8 добавлен новый тип ядра InstanceType
, который можно использовать для получения возвращаемого типа функции конструктора.
/**
* Obtain the return type of a constructor function type
*/
type InstanceType<T extends new (...args: any[]) => any> = T extends new (...args: any[]) => infer R ? R : any;
Эта функция довольно приятная, но не работает при использовании абстрактных классов,которые не имеют объявления new
в соответствии с системой типов TypeScript.
Сначала я подумал, что можно обойти это ограничение, создав аналогичный, но менее ограничительный тип (сняв защиту extends new (...args: any[]) => any
):
export type InstanceofType<T> = T extends new(...args: any[]) => infer R ? R : any;
Но он также распадается при передаче абстрактного класса, поскольку не может вывести тип возвращаемого значения и по умолчанию равен any
.Вот пример использования фиктивного DOM в качестве примера с попыткой приведения типов.
abstract class DOMNode extends Object {
public static readonly TYPE: string;
constructor() { super(); }
public get type() {
return (this.constructor as typeof DOMNode).TYPE;
}
}
class DOMText extends DOMNode {
public static readonly TYPE = 'text';
constructor() { super(); }
}
abstract class DOMElement extends DOMNode {
public static readonly TYPE = 'text';
public static readonly TAGNAME: string;
constructor() { super(); }
public get tagname() {
return (this.constructor as typeof DOMElement).TAGNAME;
}
}
class DOMElementDiv extends DOMElement {
public static readonly TAGNAME = 'div';
constructor() { super(); }
}
class DOMElementCanvas extends DOMElement {
public static readonly TAGNAME = 'canvas';
constructor() { super(); }
}
// Create a collection, which also discards specific types.
const nodes = [
new DOMElementCanvas(),
new DOMText(),
new DOMElementDiv(),
new DOMText()
];
function castNode<C extends typeof DOMNode>(instance: DOMNode, Constructor: C): InstanceofType<C> | null {
if (instance.type !== Constructor.TYPE) {
return null;
}
return instance as InstanceofType<C>;
}
// Attempt to cast the first one to an element or null.
// This gets a type of any:
const element = castNode(nodes[0], DOMElement);
console.log(element);
Можно ли как-нибудь преобразовать переменную в экземпляр переданного конструктора, если этот конструктор являетсяабстрактный класс?
ПРИМЕЧАНИЕ: я стараюсь избегать использования instanceof
, потому что JavaScript instaceof
очень проблематичен (2 разных версии одного и того же модуля имеют разные экземпляры конструктора).