Дженерики машинописного текста: T расширяет X внезапно хочет, чтобы X расширял T

Я получаю сообщение об ошибке, что мой интерфейс Event должен реализовать все свойства MyEvent, в то время как на самом деле я хочу, чтобы TS проверял, что MyEvent реализует Event.

Мое понимание дженериков заключалось в том, что я могу определить функцию, которая принимает тип T extends X в качестве аргумента, но теперь она говорит, что X doesn't extend T.

Фон

Я создаю простой менеджер событий:

// imports..

export interface Event {
  getName: () => string;
}

export class EventManager {
  private callbacks; // Typing is unimportant for this example.

  on(eventName: string, cb: <T extends Event>(event: T) => void) {
    // some code..

    this.callbacks[eventName].push(cb);
  }

  // more code..
}

Затем я пытаюсь прослушать событие (которое вызывает ошибку, которая меня смущает):

class MyEvent implements Event {
  // Implement Event interface method.
  getName(): string {
    return 'a';
  }

  // Add other random method.
  getFooBar(): string {
    return 'b';
  }
}

const eventManager = new EventManager();
eventManager.on('test', (e: MyEvent) => {
  console.log(e);
});

Ошибка:

TS2345: Argument of type '(e: MyEvent) => void' is not assignable to parameter of type '<T extends Event>(event: T) => void'.   
  Types of parameters 'e' and 'event' are incompatible.     
    Type 'T' is not assignable to type 'MyEvent'.       
      Property 'getFooBar' is missing in type 'Event' but required in type 'MyEvent'. 

Что здесь происходит? Я хочу, чтобы диспетчер событий принимал события любого типа, следовательно, <T extends Event>, но теперь он говорит, что Event должен иметь все свойства MyEvent, а MyEvent должен иметь весь интерфейс Event?

(PS: То же самое произойдет, если вместо принятия типа T я приму интерфейс Event).


person minitauros    schedule 06.01.2020    source источник


Ответы (1)


on необходимо определить как общий. Что-то вроде этого:

interface Event {
  getName: () => string;
}

class EventManager {
  private callbacks; // Typing is unimportant for this example.

  on<T extends Event>(eventName: string, cb: (event: T) => void) {
    // some code..
  }
  // more code..
}

class MyEvent implements Event {
  // Implement Event interface method.
  getName(): string {
    return 'a';
  }

  // Add other random method.
  getFooBar(): string {
    return 'b';
  }
}

class YourEvent implements Event {
  // Implement Event interface method.
  getName(): string {
    return 'a';
  }

  // Add other random method.
  getBarFoo(): string {
    return 'b';
  }
}

const eventManager = new EventManager();
eventManager.on<MyEvent>('test', (e: MyEvent) => {
  console.log(e);
});

eventManager.on<YourEvent>('test', (e: YourEvent) => {
  console.log(e);
});

Я думаю, что это просто непонимание синтаксиса с вашей стороны.

person igg    schedule 07.01.2020
comment
Красивый. На это ушло два с половиной часа, и все, что мне нужно было сделать, это сдвинуть объявление на 20 с небольшим символов влево, ха-ха. Действительно, это работает. И действительно, я нахожу странным, что тип должен быть объявлен в on, а не в функции обратного вызова, но я думаю, что тогда я не так хорошо разбираюсь в дженериках. - person minitauros; 07.01.2020
comment
Не могли бы вы ответить на последний вопрос об этом для воображаемых бонусных баллов? Если я объявлю метод on следующим образом: on(eventName: string, cb: (event: Event) => void) {, я все равно не смогу передать событие обратному вызову. В нем говорится, что в типе «Событие» отсутствуют следующие свойства типа «MyEvent»: getFooBar. Опять же, я ожидал, что это сработает, потому что MyEvent расширяет Event. - person minitauros; 07.01.2020
comment
Для меня использование: on(eventName: string, cb: (event: Event) => void) {}, eventManager.on('test', (e: MyEvent) => {}); ошибок не дает. Было бы неплохо, если бы вы могли подготовить код на игровой площадке для машинописных текстов или в stackblitz. - person igg; 07.01.2020
comment
Эй, прости за это. У меня есть пример: codesandbox.io/s/2t78m - person minitauros; 07.01.2020
comment
Теперь я понимаю, что вы имеете в виду. Я не уверен, как ответить на это, вы можете опубликовать другой вопрос. Никто, кроме меня, не увидит здесь это как комментарий - person igg; 08.01.2020
comment
В любом случае, спасибо за ответ на мой первоначальный вопрос! - person minitauros; 08.01.2020
comment
Здесь нашел ответ: stackoverflow.com/questions/49535848/ - person minitauros; 08.01.2020