A typed event emitter
Write the type TypedEmitter<Events>, where Events maps each event name to its payload type. It has two methods: on(event, listener) and emit(event, payload). The event must be a key of Events. The listener receives that event's payload type, and emit requires that event's payload type. Make each method generic over the key: on<K extends keyof Events>(…).
Start from
// Events maps an event name to its payload type.
type TypedEmitter<Events> = {
on(event: string, listener: (payload: any) => void): void;
emit(event: string, payload: any): void;
};Hints
Each hint gives away a little more. Open one only when you need it.
Hint 1
A generic method ties its arguments together: on<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void.
Hint 2
emit follows the same pattern: the payload parameter has the type Events[K].
Solution
From here on the page gives the method away. If you are still solving, go back to the hints.
TypeScript
type TypedEmitter<Events> = {
on<K extends keyof Events>(
event: K,
listener: (payload: Events[K]) => void,
): void;
emit<K extends keyof Events>(event: K, payload: Events[K]): void;
};The checks it must pass
declare const bus1: TypedEmitter<{ login: { userId: string }; resize: number }>;
bus1.on("login", (payload) => { const id: string = payload.userId; void id; });
declare const bus2: TypedEmitter<{ login: { userId: string }; resize: number }>;
bus2.on("resize", (payload) => { type _2 = Expect<Equal<typeof payload, number>>; });
declare const bus3: TypedEmitter<{ login: { userId: string }; resize: number }>;
bus3.emit("resize", 1280);
declare const bus4: TypedEmitter<{ login: { userId: string }; resize: number }>;
// @ts-expect-error
bus4.on("logn", () => {});
declare const bus5: TypedEmitter<{ login: { userId: string }; resize: number }>;
// @ts-expect-error
bus5.emit("resize", "wide");What an interviewer looks for
This is the pattern behind typed analytics, sockets and stores: one map of names to payloads, and the compiler connects the two arguments. It shows generics on a method, not only on a type.