typescript / expert
Snippet
Strictly Typed Event Emitter for Reactive Framework Engines
By mapping generic record types to event names and payloads, this pattern constructs a strongly typed event channel. Listeners automatically receive inferred event payload parameters based on the event name string.
snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
type EventMap = Record<string, any>;class TypedEmitter<Events extends EventMap> {private listeners: {[K in keyof Events]?: Array<(payload: Events[K]) => void>;} = {};on<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void {(this.listeners[event] ??= []).push(listener);}emit<K extends keyof Events>(event: K, payload: Events[K]): void {this.listeners[event]?.forEach((cb) => cb(payload));}}interface ApplicationEvents {'user:login': { id: string; timestamp: number };'cart:checkout': { totalAmount: number };}const bus = new TypedEmitter<ApplicationEvents>();bus.on('user:login', (data) => console.log(data.id.toUpperCase()));bus.emit('user:login', { id: 'u_102', timestamp: Date.now() });
Breakdown
1
type EventMap = Record<string, any>;
Base constraint requiring event contract dictionaries to map string event keys to payload structures.
2
[K in keyof Events]?: Array<(payload: Events[K]) => void>;
Mapped type that structures listener callback arrays corresponding to each registered event key.
3
on<K extends keyof Events>(event: K, listener: (payload: Events[K]) => void): void
Constrains event name to valid keys and infers the exact callback payload argument type.
4
bus.emit('user:login', { id: 'u_102', timestamp: Date.now() });
Triggers runtime emit while validating the payload shape at compile time.