type Listener<T> = (payload: T) => void;
export class TypedEmitter<Events extends Record<string, unknown>> {
private listeners: { [K in keyof Events]?: Listener<Events[K]>[] } = {};
on<K extends keyof Events>(event: K, fn: Listener<Events[K]>): () => void {
(this.listeners[event] ??= []).push(fn);
return () => this.off(event, fn);
}
off<K extends keyof Events>(event: K, fn: Listener<Events[K]>): void {
this.listeners[event] = this.listeners[event]?.filter(l => l !== fn);
}
emit<K extends keyof Events>(event: K, payload: Events[K]): void {
this.listeners[event]?.forEach(l => l(payload));
}
}
type AppEvents = {
'user:login': { id: number; name: string };
'user:logout': { id: number };
};
const bus = new TypedEmitter<AppEvents>();
bus.on('user:login', u => console.log(`hi ${u.name}`)); // u: { id; name }
bus.emit('user:login', { id: 1, name: 'Alice' }); // ✓
// bus.emit('user:login', { id: 1 }); // ✗ missing 'name'
Create a free account and build your private vault. Share publicly whenever you want.