typescript / intermediate
Snippet
Developing a Type-Safe Event Bus Architecture
An Event Bus decouples publishers from subscribers across modular application layers. Leveraging mapped types and indexed accesses (`Events[K]`), TypeScript enforces exact payload contracts for every emitted and listened event name.
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
25
26
27
28
29
type EventMap = Record<string, unknown>;type EventCallback<T> = (data: T) => void;class TypedEventBus<Events extends EventMap> {private listeners: { [K in keyof Events]?: EventCallback<Events[K]>[] } = {};on<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>): void {if (!this.listeners[event]) {this.listeners[event] = [];}this.listeners[event]!.push(callback);}emit<K extends keyof Events>(event: K, data: Events[K]): void {const callbacks = this.listeners[event];if (callbacks) {callbacks.forEach((cb) => cb(data));}}}interface AppEvents {userLoggedIn: { userId: string; timestamp: number };systemError: { code: number; message: string };}const bus = new TypedEventBus<AppEvents>();bus.on("userLoggedIn", (data) => console.log(`User ${data.userId} logged in`));bus.emit("userLoggedIn", { userId: "usr_101", timestamp: Date.now() });
Breakdown
1
type EventMap = Record<string, unknown>;
Constraint ensuring that event maps define string event keys mapped to arbitrary payload types.
2
private listeners: { [K in keyof Events]?: EventCallback<Events[K]>[] }
Uses mapped types to associate each event name strictly with callbacks matching its payload.
3
on<K extends keyof Events>(event: K, callback: EventCallback<Events[K]>): void
Registers an event subscriber restricted strictly to the defined payload shape.
4
emit<K extends keyof Events>(event: K, data: Events[K]): void
Triggers listeners while enforcing that payload matches the contract bound to event name K.