Как вернуть "this" с различными типами * generi c и заставить его работать для подклассов? - PullRequest
2 голосов
/ 08 марта 2020

В качестве примера рассмотрим следующий базовый класс:

abstract class Base<T> {
  public changeGeneric = (): Base<string> => {
    // do something so no matter what "T" currently is it will change to "string"
    return this as Base<string>
  }
}

Это работает, как и ожидалось. Но это усложняется при расширении Base:

class Extended<T> extends Base<T> {
   //...
   public additionalFunc = () => {/*...*/}
}

const extended = new Extended<number>();
extended.changeGeneric().additionalFunc(); // this line results in and error

После вызова extended.changeGeneric() тип должен быть Extended<string>, но в этом случае это (вроде бы) очевидно Base<string>. Поскольку additionalFunc не существует для типа Base<string>, возникает ошибка.

Детская площадка

Вопрос

Можно ли вернуть this с различными типами c generic в зависимости от текущего класса, из которого выполняется функция? Если нет, есть ли обходные пути?

Это действительно простой пример (например, Base может иметь больше обобщенных c типов), но я надеюсь, что это показывает проблему.

Подробнее о фактическом контексте

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

abstract class BaseConfig<T extends object, Errors = unknown> {
  protected validator: (values: T) => Errors = () => undefined as any;
  constructor(public initalValues: T){}

  public withValidation = <Validator extends (values: T ) => any>(validator: Validator) =>  {
    this.validator = validator ;
    return this as any as BaseConfig<T, ReturnType<Validator>>
  }
}

class SpecialConfig<T extends object, Errors = unknown> extends BaseConfig<T, Errors>{
  protected specialConfigEnabled = false;

  public enableSpecialConfig = () => {
    this.specialConfigEnabled = true;
    return this;
  }
}

interface Coordinate {
  x: number;
  y: number;
}

const specialConfig = new SpecialConfig<Coordinate>({x: 0, y: 0})
  // should somehow return SpecialConfig<Coordinate, boolean>, but returns BaseConfig<Coordinate, boolean> instead
  .withValidation(({x, y}) => x > y? false : true) 
  // should not be an error
  .enableSpecialConfig();

Детская площадка

...