如何为 class 之类的 EventEmitter 的事件有效负载分配 TypeScript 类型?

How can I assign a TypeScript type for event payloads for a class like EventEmitter?

我有一个 class 就像 Node 的 EventEmitter(它甚至可以是相同的 class)。

如何以可以指定每个不同事件名称的回调类型的方式输入它?

例如,我希望 "foo" 的负载类型为 number,而事件 "bar" 的负载类型为 string,如:

const emitter = /* get emitter from somewhere. And how would the type definition look like? */

emitter.on('foo', payload => {
  /* type of payload is number, enforced by TypeScript */
  testNumber('foo') // TS error, 'foo' is not a number
  testNumber(payload) // it works, payload is a number
})

emitter.on('bar', payload => {
  /* type of payload is string, enforced by TypeScript */
  testString(5) // TS error, 5 is not a string
  testString(payload) // it works, payload is a string
})

function testNumber( value: number ) {}
function testString( value: string ) {}

我们将如何定义 EventEmitter 声明,以便可以定义事件及其类型,然后让用户通过正确的类型检查来使用这些事件?

也许有一种方法可以定义 EventEmitter 的类型,以便在创建 EventEmitter 时传递一个包含所有预期类型的​​类型参数?

有没有办法在创建后动态的做?

我会从这样的事情开始:

interface Events {
  foo: number
  bar: string
}

interface EventEmitter<T> {
  on<K extends keyof T>(s: K, listener: (v: T[K]) => void): T[K];
}

declare const emitter: EventEmitter<Events>;

emitter.on('foo', (payload: any) => {
  testNumber(payload)
  testString(payload)
});

emitter.on('bar', (payload: any) => {
  testNumber(payload)
  testString(payload)
});

function testNumber( value: number ) {}
function testString( value: string ) {}

这是一个 example on TS Playground