Write a typed event emitter: on and emit should only accept the events, and the arguments, of an event map.
The emitter below works at runtime, but any event name and any arguments compile. Make it generic over an event map, so new Emitter<AppEvents>() checks every on and emit against AppEvents, and listeners get their parameter types without annotations. on returns a function that removes the listener.
type Listener = (...args: any[]) => void; class Emitter { private listeners = new Map<string, Set<Listener>>(); on(event: string, listener: Listener): () => void { const set = this.listeners.get(event) ?? new Set<Listener>(); this.listeners.set(event, set); set.add(listener); return () => { set.delete(listener); }; } emit(event: string, ...args: any[]): void { this.listeners.get(event)?.forEach((listener) => listener(...args)); }} // type AppEvents = { login: [userId: string]; logout: [] };// const events = new Emitter<AppEvents>();// events.on("login", (userId) => {}); // userId: string// events.emit("login", 42); // should be a type errorThe answer
Write it yourself first. Then compare it with a reference solution and the checks it passes.
Checking your sign-in…