Typescript - как правильно ввести член абстрактного класса generi c на основе возможных дополнительных реализаций - PullRequest
2 голосов
/ 23 февраля 2020

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

Вот игровая площадка TS, показывающая проблему - все работает нормально, и типы правильные за пределами класса, но внутренне машинописный текст сообщает об ошибках типов.

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

1 Ответ

1 голос
/ 23 февраля 2020

Полиморф c this на самом деле вам здесь не поможет; нет способа пометить класс как final в TypeScript так, как вы можете, скажем, Java ... так что всегда возможно, что полиморф c this должен ссылаться на некоторые суженный тип в некотором пока неизвестном подклассе. Например:

class Foo extends Derived {
  map(val: number) {
    return "oops";
  }
}
new Foo(7).getDefaultVal().toUpperCase(); // error at runtime

Здесь, без ошибок компиляции, расширено Derived. Но Foo.getDefaultVal() просто наследует реализацию в Derived, тип возврата которого предполагается равным DeriveMapVal<this, number>, где this будет Foo на сайте вызова. И DeriveMapVal<Foo, number> составляет string. Но реализация Foo просто возвращает number, а не string, поэтому вы получаете сообщение об ошибке: компилятор просто не может проверить, что number присваивается DeriveMapVal<this, number>.


Что касается того, как с этим бороться, я думаю, это зависит от того, что вы хотите сделать. Самое простое решение, которое я могу придумать, - это сузить реализацию подкласса, чтобы ссылаться на фактический собственный класс, а не this:

class Derived extends Generic<number> {
  getDefaultVal(): DeriveMapVal<Derived, number> {
    return 0; // okay
  }
}

Это должно работать, хотя немного странно, что вы все еще можете подкласс Derived как и прежде, и компилятор правильно видит, что Foo не может быть назначен на Derived:

class Foo extends Derived {
  map(val: number) {
    return "oops";
  }
}

new Foo(7).getDefaultVal().toUpperCase(); // error at compile time too, now
const d: Derived = new Foo(10); // error
const g: Generic<number> = new Foo(20); // error

Я имею в виду, class Foo extends Derived явно не гарантирует, что Foo extends Derived верно. Странные вещи. Пока вы можете справиться с этой морщинкой, это может быть способом для вас продолжить.

Хорошо, надеюсь, это поможет; удачи!

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

...