O
Observer Pattern
The Observer pattern defines a subscription mechanism to notify objects about events.
Code
typescript
1// Event types2type EventMap = {3 userCreated: { userId: number; email: string };4 userDeleted: { userId: number };5 orderPlaced: { orderId: string; total: number };6};7 8// Generic typed event emitter9class TypedEventEmitter<T extends Record<string, unknown>> {10 private listeners: {11 [K in keyof T]?: Array<(data: T[K]) => void>;12 } = {};13 14 on<K extends keyof T>(event: K, callback: (data: T[K]) => void): () => void {15 if (!this.listeners[event]) {16 this.listeners[event] = [];17 }18 this.listeners[event]!.push(callback);19 20 // Return unsubscribe function21 return () => this.off(event, callback);22 }23 24 off<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {25 const callbacks = this.listeners[event];26 if (callbacks) {27 const index = callbacks.indexOf(callback);28 if (index > -1) {29 callbacks.splice(index, 1);30 }31 }32 }33 34 emit<K extends keyof T>(event: K, data: T[K]): void {35 const callbacks = this.listeners[event];36 if (callbacks) {37 callbacks.forEach((callback) => callback(data));38 }39 }40 41 once<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {42 const unsubscribe = this.on(event, (data) => {43 unsubscribe();44 callback(data);45 });46 }47}48 49// Usage with application events50const events = new TypedEventEmitter<EventMap>();51 52// Subscribe to events53events.on("userCreated", ({ userId, email }) => {54 console.log(`New user: ${email} (ID: ${userId})`);55});56 57events.on("userCreated", ({ email }) => {58 console.log(`Sending welcome email to ${email}`);59});60 61const unsubscribeOrder = events.on("orderPlaced", ({ orderId, total }) => {62 console.log(`Order ${orderId}: $${total.toFixed(2)}`);63});64 65// One-time subscription66events.once("userDeleted", ({ userId }) => {67 console.log(`User ${userId} deleted (one-time notification)`);68});69 70// Emit events71events.emit("userCreated", { userId: 1, email: "alice@example.com" });72events.emit("orderPlaced", { orderId: "ORD-001", total: 99.99 });73events.emit("userDeleted", { userId: 1 });74events.emit("userDeleted", { userId: 2 }); // Won't trigger once handler75 76// Unsubscribe from order events77unsubscribeOrder();78events.emit("orderPlaced", { orderId: "ORD-002", total: 149.99 }); // Not loggedRun this example locally
$ npx ts-node patterns/observer.ts