Here's a design of an event emitter:
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>>
(eventKey: K, fn: EventReceiver<T[K]>): void;
emit<K extends EventKey<T>>
(eventKey: K, params: T[K]): void;
}
// example usage
const emitter = eventEmitter<{
data: Buffer | string;
end: undefined;
}>();
// OK!
emitter.on('data', (x: string) => {});
// Error! 'string' is not assignable to 'number'
emitter.on('data', (x: number) => {});
The design works but two things I don't understand:
EventKey
: why does it need to add astring &
here?Why do we need generics
K
withon
andemit
methods, can't the type foreventKey
param simply bekeyof T
?
Many thanks in advance.