Ошибка TypeScript при использовании этого типа в качестве общего параметра

#typescript #generics #this

#typescript #общие #это

Вопрос:

Когда я использую следующий код, я получаю ошибку, описанную в комментарии. Чего мне здесь не хватает?

 type MyEvent<S extends Bindable> = {
    source: S;
};

type MyListener<S extends Bindable> =
    (event: MyEvent<S>) => void;

interface Bindable {
    listener: MyListener<this>;
};

interface Row extends Bindable {
    id: number;
}

declare var e: MyEvent<Row>;
//                     ^
// Type 'Row' does not satisfy the constraint 'Bindable'.
//   Types of property 'listener' are incompatible.
//     Type 'MyListener<Row>' is not assignable to type 'MyListener<Bindable>'.
//       Property 'id' is missing in type 'Bindable' but required in type 'Row'.ts(2344)
  

Комментарии:

1. Какую версию Typescript вы используете?

2. Я использую typescript 4.0.2

Ответ №1:

this ссылается на Bindable, тогда как вы хотите, чтобы он ссылался на Row . Сделайте это вместо:

 type MyEvent<S extends Bindable<S>> = {
    source: S;
}

type MyListener<S extends Bindable<S>> = (event: MyEvent<S>) => void;

interface Bindable<Self extends Bindable<Self>> {
    listener: MyListener<Self>;
}

interface Row extends Bindable<Row> {
    id: number;
}

declare var e: MyEvent<Row>;
  

Редактировать: или одно небольшое изменение может быть лучше для вашего варианта использования:

 interface Bindable {
    listener: MyListener<Bindable>;
}
  

Комментарии:

1. Это именно то, чего я пытаюсь избежать, используя ‘this’. Но проблема заключается в сигнатуре функции MyListener. Если я изменю тип свойства прослушивателя на MyEvent<this> (что, конечно, не то, что я пытаюсь сделать), он не жалуется

Ответ №2:

Теперь я понимаю свою проблему. Дело в том, что следующий код нарушил бы ограничение.

 interface Unrelated extends Bindable {
   prop: number;
}

var bindable: Bindable;
declare var row: Row;
declare var listener: MyListener<Unrelated>;
bindable = row;
bindable.listener = listener;
  

поскольку MyListener<Unrelated> значение
будет присвоено MyListener<Row> свойству.

Фактически, после дальнейшего тестирования я обнаружил, что следующий код:

 interface A {
    prop: (parm: this) => void;
}

interface B extends A {
    propB: number;
}

interface C extends A {
    propC: number;
}

declare var b: B;
declare var c: C;
var a: A;
a = b;
  

имеет ту же проблему, но если я изменю interface A на:

 interface A {
   prop: this
}
  

это не так, что должно выдавать ту же ошибку, потому что я мог бы присвоить значение C реквизиту B, который имеет тип B через переменную a.