Как правильно применить определение универсального типа для универсальных статических методов? - PullRequest
1 голос
/ 24 апреля 2019

У меня есть следующие базовые и производные классы.

class GenericBase<T = any> {
  static method(id: any) {
    console.log(`${id}: ${this.name}#method`);
  }
  public someProp!: T;
}

class DerivedGeneric extends GenericBase<Date> {}

Я ищу способ правильно применять определения типов, которые позволят мне вызывать статический метод.Вот то, что я пробовал до сих пор.

const t1: typeof GenericBase = DerivedGeneric;
t1.method("t1");

type Type<T> = new (...arg: any[]) => T;
const t2: Type<GenericBase> = DerivedGeneric;
t2.method("t2");

Для первого (t1) TypeScript показывает следующую ошибку

Type 'typeof DerivedGeneric' is not assignable to type 'typeof GenericBase'.
  Type 'DerivedGeneric' is not assignable to type 'GenericBase'.
    Types of property 'someProp' are incompatible.
      Type 'Date' is not assignable to type 'T'.

Для второго он показывает следующую ошибку.

Property 'method' does not exist on type 'Type>'.

Естественно, что следующее работает без какой-либо ошибки времени компиляции ...

const t3: Function = DerivedGeneric;
(t3 as typeof DerivedGeneric).method("t3");

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

const t4: Function = () => {};
(t4 as typeof DerivedGeneric).method("t4");

Без обобщений первый подход (typeof *Base*) работает достаточно хорошо.Вы можете проверить это с этой ссылки на игровую площадку .Очевидно, что все подходы (кроме t4) работают во время выполнения, и только ошибки времени компиляции вызывают у меня проблемы.

Есть ли способ исправить типизацию с помощью обобщений?

Редактировать: Ссылка на игровую площадку со следующим типом.

type Type<T> = new (...arg: any[]) => T;
type func = Pick<typeof GenericBase, keyof typeof GenericBase> & Type<GenericBase>;

1 Ответ

1 голос
/ 24 апреля 2019

Проблема в том, что поскольку базовый класс имеет параметр универсального типа, его конструктор является универсальным конструктором. Вот как будет выглядеть подпись конструктора:

const t3 : new <T>(...arg: any[]) => GenericBase<T> = GenericBase

Именно поэтому, когда вы пытаетесь присвоить DerivedGeneric для typeof GenericBase, вы не можете этого сделать, потому что DerivedGeneric не имеет такого универсального конструктора.

Если вы просто хотите, чтобы тип представлял статику класса, вы можете использовать Pick, чтобы избавиться от сигнатуры универсального конструктора из typeof GenericBase:

const t1: Pick<typeof GenericBase, keyof typeof GenericBase> = DerivedGeneric; // OK
t1.method("t1");

Вы также можете создать пересечение конструктора, возвращающего GenericBase<any> и статических членов.

type Type<T> =  new (...args: unknown[]) => T;
const t1: Type<GenericBase> & Pick<typeof GenericBase, keyof typeof GenericBase>  = DerivedGeneric;
t1.method("t1");
new t1()

Примечание: он не будет работать с ...args: any[], any немного особенный, не уверен, как это повлияет на это, но в любом случае unknown предпочтительнее.

...