Рассмотрим эту ситуацию (фиктивный пример):
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
}
}
И здесь я не вижу разницы в типах по сравнению с первым случаем.
Причина, по которой последнее решение «работает», заключается в том, что Farmer<A> больше не зависит от Aструктурно, а это все, что действительно заботит TypeScript. То есть Farmer<A> и Farmer<B> являются эквивалентными типами, и больше нет необходимости заботиться о взаимной рекурсии.
Я не уверен, есть ли у кого-нибудь более чистое решение, которое не включает утверждения типа ... если нет, я в какой-то момент превращу эти комментарии в ответ. Ваше здоровье
@jcalz, полезные моменты. Благодарю. Я отредактировал вопрос, добавив еще один случай.






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