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, data: T[K]): void;
once<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;
}
class EventEmitter<T extends EventMap> implements Emitter<T> {
private listeners: {
[K in keyof T]?: Array<EventReceiver<T[K]>>;
} = {};
on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
if (!this.listeners[eventName]) {
this.listeners[eventName] = [];
}
this.listeners[eventName]!.push(fn);
}
off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
const handlers = this.listeners[eventName];
if (handlers) {
this.listeners[eventName] = handlers.filter(f => f !== fn);
}
}
emit<K extends EventKey<T>>(eventName: K, data: T[K]): void {
const handlers = this.listeners[eventName];
if (handlers) {
handlers.forEach(fn => fn(data));
}
}
once<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void {
const onceWrapper = ((data: T[K]) => {
fn(data);
this.off(eventName, onceWrapper);
}) as EventReceiver<T[K]>;
this.on(eventName, onceWrapper);
}
}
export default EventEmitter;