I have come across a similar implementation of an Event Emitter in TypeScript and I am looking to create a helper function that maintains type integrity. You can find my code attempt on TS Play sample page. Below is the snippet from my sample:
In the provided sample, I am only given a generic eventName: string
, and callback: (e: any) => void
as output. Is there a way to link these three arguments to the same instance of these generics?
import EventEmitter from 'events';
type EventMap = Record<string, any>;
type EventKey<T extends EventMap> = string & keyof T;
type EventReceiver<T> = (params: T) => void;
interface Emitter<T extends EventMap> {
on<K extends EventKey<T>>
(eventName: K, fn: EventReceiver<T[K]>): void;
off<K extends EventKey<T>>
(eventName: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>
(eventName: K, params: T[K]): void;
}
export class MyEmitter<T extends EventMap> implements Emitter<T> {
private emitter = new EventEmitter();
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>) {
this.emitter.on(eventName, fn);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>) {
this.emitter.off(eventName, fn);
}
emit<K extends EventKey<T>>(eventName: K, params: T[K]) {
this.emitter.emit(eventName, params);
}
}
function onEmit<
T extends EventMap,
K extends EventKey<T>
>(
emitter: MyEmitter<T>,
eventName: K,
callback: EventReceiver<T[K]>,
) {
emitter.on(eventName, callback);
}
class Foo extends MyEmitter<{ foo: number }> {}
const foo = new Foo();
foo.on('foo',
(value) => console.log('foo was', value.toFixed(2))
// ^?
)
onEmit(foo, 'foo',
(value) => console.log('foo was', value.toFixed(2))
// ^?
)