将TypeScript的InstanceType泛型与抽象类一起使用?

时间:2018-04-26 02:07:26

标签: typescript generics casting

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;

这个功能相当不错,但在使用抽象类时会崩溃,而抽象类根据TypeScript的类型系统没有new声明。

起初,我认为我可以通过创建一个类似但限制较少的类型来解决这个限制(删除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个不同版本具有不同的构造函数实例)。

1 个答案:

答案 0 :(得分:3)

您可以查询摘要prototype的{​​{1}}的类型以获取其实例的类型。这不要求类型只有class签名,只有new属性。抽象类没有prototype签名,但它们具有new属性。

这是它的样子

prototype

类型位置中的表达式function castNode<C extends typeof DOMNode>( instance: DOMNode, Constructor: C ): C['prototype'] | null { if (instance.type !== Constructor.TYPE) { return null; } return instance; } 称为索引访问类型。它是类型 C['P']中名为P的属性的的类型。