Использование обобщенных типов TypeScript InstanceType с абстрактными классами? - PullRequest
0 голосов
/ 26 апреля 2018

В 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 разных версии одного и того же модуля имеют разные экземпляры конструктора).

1 Ответ

0 голосов
/ 26 апреля 2018

Вы можете запросить тип prototype абстрактного 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 в типе C.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...