网站搜索

Typescript 泛型:T extends X 突然想让 X extends T


我收到一个错误,提示我的接口 Event 应该实现 MyEvent 的所有属性,而实际上我希望 TypeScript 检查 MyEvent 是否实现了 Event。

我对泛型的理解是我可以定义一个函数,接受类型 T extends X 作为参数,但现在它显示 X 不继承 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 接口的所有属性?

(附注:如果我不接受类型 T 而是接受 Event 接口,也会发生同样的情况)。

答案#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);
});

我认为这只是你在语法上的误解。