Вызов конструктора из метода подкласса с универсальным параметром - PullRequest
0 голосов
/ 20 октября 2018

Я не могу получить следующий код для проверки типа:

type MyFunctionConstructor<T, F extends MyFunction<T>> = new (
  f: (n: number) => T
) => F;

class MyFunction<T> {
  constructor(f: (n: number) => T) {
    this.f = f;
  }

  f: (n: number) => T;

  composeT(g: (t: T) => T) {
    return new (this.constructor as MyFunctionConstructor<T, this>)(n =>
      g(this.f(n))
    );
  }

  composeU<U>(g: (t: T) => U) {
    return new (this.constructor as MyFunctionConstructor<U, this>)(n =>
      g(this.f(n)) // tsc error here, see below
    );
  }
}

class MyFancyFunction<T> extends MyFunction<T> {}

Я получаю следующую ошибку:

Type 'this' does not satisfy the constraint 'MyFunction<U>'.
Type 'MyFunction<T>' is not assignable to type 'MyFunction<U>'.
Type 'T' is not assignable to type 'U'.

Я не хочу вызывать конструктор с помощьюимя (т. е. new MyFunction(...)), так что если f является экземпляром подкласса MyFunction (например, FancyFunction), то так же f.composeT(g) и f.composeU(g).Приведение as, используемое для вызова конструктора в composeT, не работает для более общего метода composeU, который имеет универсальный параметр.Как я могу иметь дело с дополнительным универсальным, U?

(Метод для composeT проверки типа происходит от этого ответа . Этот вопрос по сути является продолжениемчто я не смог уместить в комментарии.)

1 Ответ

0 голосов
/ 20 октября 2018

Как я упоминал в комментариях, это не может быть выражено в системе типов TypeScript (во всяком случае, с TS3.1).TypeScript очень ограничен в своей способности представлять так называемые типы с более высоким родом .

Во-первых, вы хотите сказать, что все подклассы MyFunction<T> должны быть общими в T.То есть вам не нужно расширять тип MyFunction<T>, а конструктор типа T ⇒ MyFunction<T>, который преобразует тип T в MyFunction<T>.Но вы не можете этого сделать, потому что нет общего способа ссылаться на конструкторы типов в TypeScript (Microsoft / TypeScript # 1213) .

Далее, при условии, что вы можете расширить T ⇒ MyFunction<T> ине MyFunction<T>, вам понадобится полиморфный * TypeScript this, чтобы учесть это, так что this также является конструктором типа, а this<X> - конкретным типом.И вы тоже не можете этого сделать (Microsoft / TypeScript # 5845) .

Поскольку Microsoft / TypeScript # 1213 по-прежнему остается открытой проблемой и находится в состоянии «требуется помощь», существуетнекоторые надеются, что в конечном итоге вы сможете сделать это.Но я не задерживал дыхание.Если вы посмотрите на эту проблему, вы увидите некоторые обходные пути, которые используют некоторые люди, но, на мой взгляд, они слишком громоздки, чтобы я рекомендовал.

Вы можете попробовать что-то вроде:

  composeU<U>(g: (t: T) => U): MyFunction<U> {
    return new (this.constructor as any)((n: number) =>
      g(this.f(n)); 
    );
  }

но вам нужно будет явно сузить определения для каждого подкласса, если вы хотите охватить дух полиморфизма this:

class MyFancyFunction<T> extends MyFunction<T> { }
interface MyFancyFunction<T> {
  composeU<U>(g: (t: T) => U): MyFancyFunction<U>;
}

В приведенном выше описании мы используем объявление слияния для суженияcomposeU метод MyFancyFunction.

В любом случае, надеюсь, что это поможет.Удачи!

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