Typescript3.1.3 + Generics, ошибка в присвоении типа

Рассмотрим эту ситуацию (фиктивный пример):

class Farmer<A extends Animal<Farmer<A>>>
{
    public animal: A;
    constructor() {
        let a = new Animal(this);
        // ...
    }
}

class Animal<F extends Farmer<Animal<F>>>
{
    public farmer: F;
    constructor(f: F) {
        this.farmer = f;
    }
}

Кто-нибудь может объяснить, почему в конструкторе Farmer (где я передаю аргумент this в new Animal) приведенный выше код вызывает эту ошибку?

TS2345: Argument of type 'this' is not assignable to parameter of type 'Farmer<Animal<this>>'.
 Type 'Farmer<A>' is not assignable to type 'Farmer<Animal<this>>'.
  Type 'A' is not assignable to type 'Animal<this>'.
   Type 'Animal<Farmer<A>>' is not assignable to type 'Animal<this>'.
    Type 'Farmer<A>' is not assignable to type 'this'.

Следующие действия решают проблему:

class Farmer<A extends Animal<Farmer<A>>>
{
    public animal: A;
    constructor() {
        let a = new Animal(this as Farmer<A>); // works fine
    }
}

Или как альтернатива:

class Farmer<A extends Animal<Farmer<A>>>
{
    public animal: A;
    constructor() {
        let a = new Animal<Farmer<A>>(this); // works fine
    }
}

Более странным для меня является следующее:

class Farmer<A extends Animal<Farmer<A>>>
{
    // public animal: A; // removed this line
    constructor() {
        let a = new Animal(this); // now this one works fine too
    }
}

Хотя первые два решения довольно ясны, последнее вызывает у меня большие сомнения. Кто-нибудь может объяснить, что случилось?

Редактировать

Следующее нормально (рассмотрите необязательный параметр в конструкторе Animal):

class Farmer<A extends Animal<Farmer<A>>>
{
    public animal: A;
    constructor() {
        let a = new Animal();
        a.farmer = this; // works fine
    }
}

И здесь я не вижу разницы в типах по сравнению с первым случаем.

Вау, обычно в TypeScript вы можете обойти F-ограниченный полиморфизм с помощью полиморфный this, но здесь у вас есть параметры рекурсивного типа взаимно, так что вы не можете. Компилятор не может проверить, что A эквивалентен Animal<Farmer<A>> (он знает только, что A является его подтипом), поэтому он блокирует new Animal(this) без этих утверждения типа.

jcalz 11.11.2018 21:27

Причина, по которой последнее решение «работает», заключается в том, что Farmer<A> больше не зависит от Aструктурно, а это все, что действительно заботит TypeScript. То есть Farmer<A> и Farmer<B> являются эквивалентными типами, и больше нет необходимости заботиться о взаимной рекурсии.

jcalz 11.11.2018 21:28

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

jcalz 11.11.2018 21:29

@jcalz, полезные моменты. Благодарю. Я отредактировал вопрос, добавив еще один случай.

Andrea 12.11.2018 08:29
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
4
54
0

Другие вопросы по теме