+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 105 of 355

📡 Event Emitters with TypeScript: Type-Safe Events Mastery

Master type-safe event-driven programming in TypeScript with custom event emitters, advanced patterns, and bulletproof event handling 🎯

🚀Intermediate
22 min read

Prerequisites

  • Basic TypeScript syntax and types 📝
  • Understanding of Promises and async programming ⚡
  • Object-oriented programming concepts 🏗️

What you'll learn

  • Master type-safe event emitters for robust event-driven architectures 📡
  • Build scalable communication systems with custom event patterns 🌐
  • Handle complex event flows with error resilience and performance optimization 🛡️
  • Create production-ready event systems for real-world applications 🏭

🎯 Introduction

Welcome to the powerful world of type-safe event-driven programming with TypeScript! 🎉 In this guide, we’ll explore how to build robust communication systems that connect different parts of your application through elegant, type-safe events.

You’ll discover how to transform tightly-coupled code into loosely-coupled, maintainable systems that scale beautifully. Whether you’re building user interfaces 🖼️, real-time applications 📡, or complex business logic 🏢, mastering event emitters is essential for creating flexible, responsive TypeScript applications.

By the end of this tutorial, you’ll be architecting event-driven systems that communicate like a well-orchestrated symphony! 📡 Let’s dive in! 🏊‍♂️

📚 Understanding Event Emitters

🤔 What are Event Emitters?

Event emitters are like having a sophisticated messaging system within your application 📬. Think of them as the nervous system of your code - when something happens in one part, other parts can instantly react without being directly connected!

In TypeScript terms, event emitters provide:

  • Decoupled communication - components communicate without direct dependencies
  • 🚀 Type safety - compile-time checking for event names and payload types
  • 🛡️ Flexible architecture - easy to add new listeners without modifying emitters
  • 📦 Async support - handle both synchronous and asynchronous event processing

💡 Why Use Event Emitters?

Here’s why event emitters are architectural game-changers:

  1. Loose Coupling 🔗: Components don’t need to know about each other directly
  2. Scalability 📈: Easy to add new features without breaking existing code
  3. Testability 🧪: Mock events easily for isolated testing
  4. Maintainability 🔧: Changes in one component don’t cascade through the system
  5. Real-Time Features ⚡: Perfect for live updates and reactive interfaces

Real-world example: When a user places an order, instead of calling inventory, payment, and notification services directly, you emit an “order-placed” event and let each service react independently! 🛒

🔧 Basic Event Emitter Patterns

📝 Simple Type-Safe Event Emitter

Let’s start with fundamental event emitter patterns:

// 🎯 Basic type-safe event emitter implementation
// TypeScript ensures event names and payloads match exactly

// 🏗️ Define event payload types
interface EventMap {
  'user-login': { userId: string; timestamp: Date; device: string };
  'user-logout': { userId: string; sessionDuration: number };
  'order-created': { orderId: string; userId: string; total: number; items: OrderItem[] };
  'payment-processed': { orderId: string; amount: number; method: PaymentMethod };
  'notification-sent': { userId: string; type: NotificationType; content: string };
}

// 🔧 Supporting types
interface OrderItem {
  productId: string;
  quantity: number;
  price: number;
  name: string;
}

type PaymentMethod = 'credit_card' | 'paypal' | 'bank_transfer' | 'crypto';
type NotificationType = 'email' | 'sms' | 'push' | 'in_app';

// 📡 Type-safe event emitter class
class TypeSafeEventEmitter<TEventMap = Record<string, any>> {
  private listeners: Map<keyof TEventMap, Set<Function>> = new Map();
  private onceListeners: Map<keyof TEventMap, Set<Function>> = new Map();
  private maxListeners: number = 10;
  
  // 👂 Add event listener with type safety
  on<K extends keyof TEventMap>(
    event: K,
    listener: (payload: TEventMap[K]) => void | Promise<void>
  ): this {
    console.log(`👂 Adding listener for event: ${String(event)}`);
    
    if (!this.listeners.has(event)) {
      this.listeners.set(event, new Set());
    }
    
    const eventListeners = this.listeners.get(event)!;
    
    // 🚨 Check max listeners to prevent memory leaks
    if (eventListeners.size >= this.maxListeners) {
      console.warn(`⚠️ Too many listeners (${eventListeners.size}) for event: ${String(event)}`);
    }
    
    eventListeners.add(listener);
    return this;
  }
  
  // 🎯 Add one-time listener
  once<K extends keyof TEventMap>(
    event: K,
    listener: (payload: TEventMap[K]) => void | Promise<void>
  ): this {
    console.log(`🎯 Adding one-time listener for event: ${String(event)}`);
    
    if (!this.onceListeners.has(event)) {
      this.onceListeners.set(event, new Set());
    }
    
    this.onceListeners.get(event)!.add(listener);
    return this;
  }
  
  // 📤 Emit event with type-safe payload
  async emit<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): Promise<void> {
    console.log(`📤 Emitting event: ${String(event)}`);
    
    const promises: Promise<void>[] = [];
    
    // 🔄 Execute regular listeners
    const regularListeners = this.listeners.get(event);
    if (regularListeners) {
      for (const listener of regularListeners) {
        promises.push(this.executeListener(listener, payload));
      }
    }
    
    // 🎯 Execute one-time listeners and remove them
    const onceListeners = this.onceListeners.get(event);
    if (onceListeners) {
      for (const listener of onceListeners) {
        promises.push(this.executeListener(listener, payload));
      }
      this.onceListeners.delete(event); // 🗑️ Remove after execution
    }
    
    // ⏳ Wait for all listeners to complete
    await Promise.all(promises);
    console.log(`✅ Event ${String(event)} processing complete`);
  }
  
  // ⚡ Execute listener with error handling
  private async executeListener(listener: Function, payload: any): Promise<void> {
    try {
      const result = listener(payload);
      
      // 🔄 Handle async listeners
      if (result instanceof Promise) {
        await result;
      }
    } catch (error) {
      console.error('💥 Listener execution error:', error.message);
      // 🛡️ Don't let one listener failure break others
    }
  }
  
  // 🗑️ Remove listener
  off<K extends keyof TEventMap>(
    event: K,
    listener: (payload: TEventMap[K]) => void | Promise<void>
  ): this {
    console.log(`🗑️ Removing listener for event: ${String(event)}`);
    
    const eventListeners = this.listeners.get(event);
    if (eventListeners) {
      eventListeners.delete(listener);
      
      // 🧹 Clean up empty sets
      if (eventListeners.size === 0) {
        this.listeners.delete(event);
      }
    }
    
    return this;
  }
  
  // 🧹 Remove all listeners for an event
  removeAllListeners<K extends keyof TEventMap>(event?: K): this {
    if (event) {
      console.log(`🧹 Removing all listeners for event: ${String(event)}`);
      this.listeners.delete(event);
      this.onceListeners.delete(event);
    } else {
      console.log('🧹 Removing all listeners for all events');
      this.listeners.clear();
      this.onceListeners.clear();
    }
    
    return this;
  }
  
  // 📊 Get listener count
  listenerCount<K extends keyof TEventMap>(event: K): number {
    const regular = this.listeners.get(event)?.size || 0;
    const once = this.onceListeners.get(event)?.size || 0;
    return regular + once;
  }
  
  // ⚙️ Set max listeners
  setMaxListeners(n: number): this {
    this.maxListeners = n;
    return this;
  }
}

// 🎮 Usage example with type safety
const eventBus = new TypeSafeEventEmitter<EventMap>();

// ✅ Type-safe event listening
eventBus.on('user-login', async (data) => {
  // 🎯 TypeScript knows data has userId, timestamp, device properties
  console.log(`👤 User ${data.userId} logged in from ${data.device}`);
  
  // 📊 Track login analytics
  await trackUserLogin(data.userId, data.device);
});

eventBus.on('order-created', async (order) => {
  // 🛒 TypeScript knows order has orderId, userId, total, items properties
  console.log(`🛒 New order ${order.orderId} for $${order.total}`);
  
  // 📧 Send confirmation email
  await sendOrderConfirmation(order.userId, order.orderId);
  
  // 📦 Update inventory
  await updateInventory(order.items);
});

// 🚫 TypeScript will catch type errors at compile time
// eventBus.emit('user-login', { invalidProperty: 'value' }); // ❌ Compile error!

🔄 Advanced Event Flow Patterns

Let’s explore sophisticated event patterns:

// 🏭 Advanced event-driven system with middleware and validation
class AdvancedEventEmitter<TEventMap = Record<string, any>> extends TypeSafeEventEmitter<TEventMap> {
  private middleware: EventMiddleware<TEventMap>[] = [];
  private validators: Map<keyof TEventMap, EventValidator<any>> = new Map();
  private eventHistory: EventHistoryItem<TEventMap>[] = [];
  private maxHistory: number = 1000;
  
  // 🔧 Add middleware for event processing
  use(middleware: EventMiddleware<TEventMap>): this {
    console.log('🔧 Adding event middleware');
    this.middleware.push(middleware);
    return this;
  }
  
  // ✅ Add validator for specific event type
  setValidator<K extends keyof TEventMap>(
    event: K,
    validator: EventValidator<TEventMap[K]>
  ): this {
    console.log(`✅ Setting validator for event: ${String(event)}`);
    this.validators.set(event, validator);
    return this;
  }
  
  // 📤 Enhanced emit with middleware and validation
  async emit<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): Promise<void> {
    const eventData: EventContext<TEventMap, K> = {
      event,
      payload,
      timestamp: new Date(),
      metadata: {
        emitterId: this.generateEmitterId(),
        source: 'advanced-emitter'
      }
    };
    
    try {
      // ✅ Validate payload
      await this.validateEvent(event, payload);
      
      // 🔧 Run through middleware pipeline
      const processedData = await this.runMiddleware(eventData);
      
      // 📊 Record in history
      this.recordEvent(processedData);
      
      // 📤 Emit the processed event
      await super.emit(event, processedData.payload);
      
    } catch (error) {
      console.error(`💥 Event emission failed for ${String(event)}:`, error.message);
      
      // 📤 Emit error event
      await this.emitError(event, payload, error);
      throw error;
    }
  }
  
  // ✅ Validate event payload
  private async validateEvent<K extends keyof TEventMap>(
    event: K,
    payload: TEventMap[K]
  ): Promise<void> {
    const validator = this.validators.get(event);
    if (validator) {
      console.log(`✅ Validating event: ${String(event)}`);
      
      const result = await validator.validate(payload);
      if (!result.isValid) {
        throw new Error(`Validation failed: ${result.errors.join(', ')}`);
      }
    }
  }
  
  // 🔧 Run middleware pipeline
  private async runMiddleware<K extends keyof TEventMap>(
    eventData: EventContext<TEventMap, K>
  ): Promise<EventContext<TEventMap, K>> {
    let currentData = eventData;
    
    for (const middleware of this.middleware) {
      console.log('🔧 Running middleware...');
      currentData = await middleware.process(currentData);
    }
    
    return currentData;
  }
  
  // 📊 Record event in history
  private recordEvent<K extends keyof TEventMap>(eventData: EventContext<TEventMap, K>): void {
    this.eventHistory.push({
      event: eventData.event,
      timestamp: eventData.timestamp,
      metadata: eventData.metadata
    });
    
    // 🧹 Maintain history size limit
    if (this.eventHistory.length > this.maxHistory) {
      this.eventHistory = this.eventHistory.slice(-this.maxHistory);
    }
  }
  
  // 💥 Emit error event
  private async emitError<K extends keyof TEventMap>(
    originalEvent: K,
    payload: TEventMap[K],
    error: Error
  ): Promise<void> {
    try {
      // 📤 Emit system error event (if it exists in the event map)
      if ('system-error' in this.listeners || 'system-error' in this.onceListeners) {
        await super.emit('system-error' as K, {
          originalEvent: String(originalEvent),
          error: error.message,
          timestamp: new Date()
        } as TEventMap[K]);
      }
    } catch (emitError) {
      console.error('💥 Failed to emit error event:', emitError.message);
    }
  }
  
  // 📊 Generate unique emitter ID
  private generateEmitterId(): string {
    return `emitter-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
  
  // 📜 Get event history
  getEventHistory(): EventHistoryItem<TEventMap>[] {
    return [...this.eventHistory];
  }
  
  // 📊 Get event statistics
  getEventStats(): EventStats<TEventMap> {
    const stats: EventStats<TEventMap> = {} as EventStats<TEventMap>;
    
    for (const historyItem of this.eventHistory) {
      const event = historyItem.event;
      if (!stats[event]) {
        stats[event] = { count: 0, lastEmitted: historyItem.timestamp };
      }
      stats[event].count++;
      if (historyItem.timestamp > stats[event].lastEmitted) {
        stats[event].lastEmitted = historyItem.timestamp;
      }
    }
    
    return stats;
  }
}

// 🏗️ Supporting interfaces
interface EventContext<TEventMap, K extends keyof TEventMap> {
  event: K;
  payload: TEventMap[K];
  timestamp: Date;
  metadata: EventMetadata;
}

interface EventMetadata {
  emitterId: string;
  source: string;
  [key: string]: any;
}

interface EventMiddleware<TEventMap> {
  process<K extends keyof TEventMap>(
    eventData: EventContext<TEventMap, K>
  ): Promise<EventContext<TEventMap, K>>;
}

interface EventValidator<T> {
  validate(payload: T): Promise<ValidationResult>;
}

interface ValidationResult {
  isValid: boolean;
  errors: string[];
}

interface EventHistoryItem<TEventMap> {
  event: keyof TEventMap;
  timestamp: Date;
  metadata: EventMetadata;
}

type EventStats<TEventMap> = {
  [K in keyof TEventMap]: {
    count: number;
    lastEmitted: Date;
  };
};

// 🔧 Concrete middleware implementations
class LoggingMiddleware<TEventMap> implements EventMiddleware<TEventMap> {
  async process<K extends keyof TEventMap>(
    eventData: EventContext<TEventMap, K>
  ): Promise<EventContext<TEventMap, K>> {
    console.log(`📝 [MIDDLEWARE] Event ${String(eventData.event)} at ${eventData.timestamp.toISOString()}`);
    return eventData;
  }
}

class EnrichmentMiddleware<TEventMap> implements EventMiddleware<TEventMap> {
  async process<K extends keyof TEventMap>(
    eventData: EventContext<TEventMap, K>
  ): Promise<EventContext<TEventMap, K>> {
    console.log('✨ [MIDDLEWARE] Enriching event data...');
    
    // 📊 Add enrichment data
    eventData.metadata.enrichedAt = new Date();
    eventData.metadata.processingId = `proc-${Math.random().toString(36).substr(2, 9)}`;
    
    return eventData;
  }
}

// ✅ Concrete validator implementation
class UserLoginValidator implements EventValidator<EventMap['user-login']> {
  async validate(payload: EventMap['user-login']): Promise<ValidationResult> {
    const errors: string[] = [];
    
    // 🔍 Validate user ID
    if (!payload.userId || payload.userId.trim() === '') {
      errors.push('userId is required');
    }
    
    // 🔍 Validate device
    if (!payload.device || payload.device.trim() === '') {
      errors.push('device is required');
    }
    
    // 🔍 Validate timestamp
    if (!payload.timestamp || payload.timestamp > new Date()) {
      errors.push('timestamp must be valid and not in the future');
    }
    
    return {
      isValid: errors.length === 0,
      errors
    };
  }
}

🌐 Real-World Event Systems

🏢 Enterprise Event Bus

Let’s build a comprehensive enterprise-grade event system:

// 🏢 Enterprise event bus with namespacing and routing
class EnterpriseEventBus {
  private namespaces: Map<string, AdvancedEventEmitter<any>> = new Map();
  private globalEmitter: AdvancedEventEmitter<GlobalEventMap>;
  private routingRules: RoutingRule[] = [];
  private eventQueue: QueuedEvent[] = [];
  private isProcessingQueue = false;
  
  constructor() {
    this.globalEmitter = new AdvancedEventEmitter<GlobalEventMap>();
    this.setupGlobalMiddleware();
  }
  
  // 🌐 Get or create namespace
  namespace<T = Record<string, any>>(name: string): AdvancedEventEmitter<T> {
    if (!this.namespaces.has(name)) {
      console.log(`🌐 Creating namespace: ${name}`);
      const emitter = new AdvancedEventEmitter<T>();
      this.setupNamespaceIntegration(name, emitter);
      this.namespaces.set(name, emitter);
    }
    
    return this.namespaces.get(name)! as AdvancedEventEmitter<T>;
  }
  
  // 🔀 Add routing rule for cross-namespace communication
  addRoutingRule(rule: RoutingRule): this {
    console.log(`🔀 Adding routing rule: ${rule.from.namespace}.${rule.from.event}${rule.to.namespace}.${rule.to.event}`);
    this.routingRules.push(rule);
    return this;
  }
  
  // 📤 Broadcast event to all namespaces
  async broadcast<T>(event: string, payload: T): Promise<void> {
    console.log(`📢 Broadcasting event: ${event}`);
    
    const promises: Promise<void>[] = [];
    
    // 📡 Send to global emitter
    promises.push(this.globalEmitter.emit('global-broadcast' as any, {
      event,
      payload,
      timestamp: new Date()
    }));
    
    // 📡 Send to all namespaces
    for (const [namespaceName, emitter] of this.namespaces) {
      promises.push(
        emitter.emit(event as any, payload).catch(error => {
          console.error(`💥 Broadcast failed for namespace ${namespaceName}:`, error.message);
        })
      );
    }
    
    await Promise.all(promises);
  }
  
  // 📊 Get system metrics
  getMetrics(): SystemMetrics {
    const namespaceMetrics: Record<string, any> = {};
    
    for (const [name, emitter] of this.namespaces) {
      namespaceMetrics[name] = emitter.getEventStats();
    }
    
    return {
      totalNamespaces: this.namespaces.size,
      globalEvents: this.globalEmitter.getEventStats(),
      namespaces: namespaceMetrics,
      routingRules: this.routingRules.length,
      queuedEvents: this.eventQueue.length
    };
  }
  
  // 🔧 Setup global middleware
  private setupGlobalMiddleware(): void {
    this.globalEmitter.use(new LoggingMiddleware());
    this.globalEmitter.use(new EnrichmentMiddleware());
  }
  
  // 🌐 Setup namespace integration
  private setupNamespaceIntegration(namespaceName: string, emitter: AdvancedEventEmitter<any>): void {
    // 🔗 Intercept namespace events for routing
    const originalEmit = emitter.emit.bind(emitter);
    
    emitter.emit = async (event: any, payload: any) => {
      // 🔀 Check routing rules
      await this.processRoutingRules(namespaceName, event, payload);
      
      // 📤 Emit original event
      return originalEmit(event, payload);
    };
  }
  
  // 🔀 Process routing rules
  private async processRoutingRules(sourceNamespace: string, sourceEvent: string, payload: any): Promise<void> {
    for (const rule of this.routingRules) {
      if (rule.from.namespace === sourceNamespace && rule.from.event === sourceEvent) {
        console.log(`🔀 Routing ${sourceNamespace}.${sourceEvent}${rule.to.namespace}.${rule.to.event}`);
        
        try {
          const targetEmitter = this.namespace(rule.to.namespace);
          
          // 🔄 Transform payload if transformer provided
          const transformedPayload = rule.transformer 
            ? await rule.transformer(payload)
            : payload;
          
          await targetEmitter.emit(rule.to.event as any, transformedPayload);
          
        } catch (error) {
          console.error(`💥 Routing failed:`, error.message);
        }
      }
    }
  }
  
  // 📦 Queue event for later processing
  queueEvent(namespaceName: string, event: string, payload: any, delay?: number): void {
    console.log(`📦 Queueing event: ${namespaceName}.${event}`);
    
    this.eventQueue.push({
      namespaceName,
      event,
      payload,
      scheduledAt: new Date(Date.now() + (delay || 0)),
      id: this.generateEventId()
    });
    
    this.processQueue();
  }
  
  // ⚡ Process event queue
  private async processQueue(): Promise<void> {
    if (this.isProcessingQueue) return;
    
    this.isProcessingQueue = true;
    
    try {
      while (this.eventQueue.length > 0) {
        const now = new Date();
        const dueEvents = this.eventQueue.filter(e => e.scheduledAt <= now);
        
        if (dueEvents.length === 0) {
          // ⏳ Wait for next due event
          const nextEvent = this.eventQueue.reduce((earliest, current) => 
            current.scheduledAt < earliest.scheduledAt ? current : earliest
          );
          
          const delay = nextEvent.scheduledAt.getTime() - now.getTime();
          setTimeout(() => this.processQueue(), delay);
          break;
        }
        
        // 📤 Process due events
        for (const queuedEvent of dueEvents) {
          try {
            const emitter = this.namespace(queuedEvent.namespaceName);
            await emitter.emit(queuedEvent.event as any, queuedEvent.payload);
            
            // 🗑️ Remove from queue
            this.eventQueue = this.eventQueue.filter(e => e.id !== queuedEvent.id);
            
          } catch (error) {
            console.error(`💥 Queued event processing failed:`, error.message);
          }
        }
      }
    } finally {
      this.isProcessingQueue = false;
    }
  }
  
  // 🆔 Generate unique event ID
  private generateEventId(): string {
    return `event-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
  }
}

// 🏗️ Supporting interfaces for enterprise bus
interface GlobalEventMap {
  'global-broadcast': { event: string; payload: any; timestamp: Date };
  'system-error': { error: string; timestamp: Date; source: string };
  'system-shutdown': { reason: string; timestamp: Date };
}

interface RoutingRule {
  from: { namespace: string; event: string };
  to: { namespace: string; event: string };
  transformer?: (payload: any) => Promise<any> | any;
}

interface QueuedEvent {
  id: string;
  namespaceName: string;
  event: string;
  payload: any;
  scheduledAt: Date;
}

interface SystemMetrics {
  totalNamespaces: number;
  globalEvents: any;
  namespaces: Record<string, any>;
  routingRules: number;
  queuedEvents: number;
}

// 🏢 Real-world usage example
class ECommerceEventSystem {
  private eventBus: EnterpriseEventBus;
  private userEvents: AdvancedEventEmitter<UserEventMap>;
  private orderEvents: AdvancedEventEmitter<OrderEventMap>;
  private inventoryEvents: AdvancedEventEmitter<InventoryEventMap>;
  private notificationEvents: AdvancedEventEmitter<NotificationEventMap>;
  
  constructor() {
    this.eventBus = new EnterpriseEventBus();
    
    // 🌐 Setup namespaces
    this.userEvents = this.eventBus.namespace<UserEventMap>('users');
    this.orderEvents = this.eventBus.namespace<OrderEventMap>('orders');
    this.inventoryEvents = this.eventBus.namespace<InventoryEventMap>('inventory');
    this.notificationEvents = this.eventBus.namespace<NotificationEventMap>('notifications');
    
    this.setupEventRouting();
    this.setupEventHandlers();
  }
  
  // 🔀 Setup cross-system event routing
  private setupEventRouting(): void {
    // 🛒 Order created → Update inventory
    this.eventBus.addRoutingRule({
      from: { namespace: 'orders', event: 'order-created' },
      to: { namespace: 'inventory', event: 'reserve-items' },
      transformer: (order: OrderEventMap['order-created']) => ({
        orderId: order.id,
        items: order.items.map(item => ({
          productId: item.productId,
          quantity: item.quantity
        }))
      })
    });
    
    // 🛒 Order created → Send notification
    this.eventBus.addRoutingRule({
      from: { namespace: 'orders', event: 'order-created' },
      to: { namespace: 'notifications', event: 'send-order-confirmation' },
      transformer: (order: OrderEventMap['order-created']) => ({
        userId: order.userId,
        orderId: order.id,
        total: order.total
      })
    });
    
    // 👤 User registered → Send welcome notification
    this.eventBus.addRoutingRule({
      from: { namespace: 'users', event: 'user-registered' },
      to: { namespace: 'notifications', event: 'send-welcome-email' },
      transformer: (user: UserEventMap['user-registered']) => ({
        userId: user.id,
        email: user.email,
        name: user.name
      })
    });
  }
  
  // 🎛️ Setup event handlers
  private setupEventHandlers(): void {
    // 👤 User event handlers
    this.userEvents.on('user-registered', async (user) => {
      console.log(`👤 Processing user registration: ${user.email}`);
      await this.createUserProfile(user);
      await this.setupUserPreferences(user.id);
    });
    
    // 🛒 Order event handlers
    this.orderEvents.on('order-created', async (order) => {
      console.log(`🛒 Processing order: ${order.id}`);
      await this.validateOrder(order);
      await this.processPayment(order);
    });
    
    this.orderEvents.on('payment-failed', async (failure) => {
      console.log(`💳 Payment failed for order: ${failure.orderId}`);
      await this.handlePaymentFailure(failure);
    });
    
    // 📦 Inventory event handlers
    this.inventoryEvents.on('reserve-items', async (reservation) => {
      console.log(`📦 Reserving items for order: ${reservation.orderId}`);
      await this.reserveInventory(reservation);
    });
    
    this.inventoryEvents.on('low-stock-alert', async (alert) => {
      console.log(`⚠️ Low stock alert: ${alert.productId}`);
      await this.handleLowStock(alert);
    });
    
    // 📧 Notification event handlers
    this.notificationEvents.on('send-order-confirmation', async (notification) => {
      console.log(`📧 Sending order confirmation: ${notification.orderId}`);
      await this.sendOrderConfirmationEmail(notification);
    });
    
    this.notificationEvents.on('send-welcome-email', async (welcome) => {
      console.log(`📧 Sending welcome email: ${welcome.email}`);
      await this.sendWelcomeEmail(welcome);
    });
  }
  
  // 🚀 Public methods for triggering events
  async registerUser(userData: Omit<UserEventMap['user-registered'], 'registeredAt'>): Promise<void> {
    await this.userEvents.emit('user-registered', {
      ...userData,
      registeredAt: new Date()
    });
  }
  
  async createOrder(orderData: Omit<OrderEventMap['order-created'], 'createdAt'>): Promise<void> {
    await this.orderEvents.emit('order-created', {
      ...orderData,
      createdAt: new Date()
    });
  }
  
  async reportLowStock(productId: string, currentStock: number, threshold: number): Promise<void> {
    await this.inventoryEvents.emit('low-stock-alert', {
      productId,
      currentStock,
      threshold,
      alertedAt: new Date()
    });
  }
  
  // 🛠️ Business logic methods (simplified for example)
  private async createUserProfile(user: UserEventMap['user-registered']): Promise<void> {
    console.log(`🔧 Creating user profile for: ${user.email}`);
    // Database operations would go here
  }
  
  private async setupUserPreferences(userId: string): Promise<void> {
    console.log(`⚙️ Setting up preferences for user: ${userId}`);
    // Default preferences setup would go here
  }
  
  private async validateOrder(order: OrderEventMap['order-created']): Promise<void> {
    console.log(`✅ Validating order: ${order.id}`);
    // Order validation logic would go here
  }
  
  private async processPayment(order: OrderEventMap['order-created']): Promise<void> {
    console.log(`💳 Processing payment for order: ${order.id}`);
    
    // Simulate payment processing
    const success = Math.random() > 0.1; // 90% success rate
    
    if (success) {
      await this.orderEvents.emit('payment-processed', {
        orderId: order.id,
        amount: order.total,
        method: 'credit_card',
        processedAt: new Date()
      });
    } else {
      await this.orderEvents.emit('payment-failed', {
        orderId: order.id,
        amount: order.total,
        reason: 'Insufficient funds',
        failedAt: new Date()
      });
    }
  }
  
  private async handlePaymentFailure(failure: OrderEventMap['payment-failed']): Promise<void> {
    console.log(`🚨 Handling payment failure: ${failure.reason}`);
    
    // Send failure notification
    await this.notificationEvents.emit('send-payment-failure-notification', {
      orderId: failure.orderId,
      reason: failure.reason
    });
  }
  
  private async reserveInventory(reservation: InventoryEventMap['reserve-items']): Promise<void> {
    console.log(`📦 Reserving inventory for order: ${reservation.orderId}`);
    // Inventory reservation logic would go here
  }
  
  private async handleLowStock(alert: InventoryEventMap['low-stock-alert']): Promise<void> {
    console.log(`⚠️ Handling low stock for product: ${alert.productId}`);
    
    // Auto-reorder if below critical threshold
    if (alert.currentStock < alert.threshold * 0.5) {
      console.log(`🔄 Auto-reordering product: ${alert.productId}`);
    }
  }
  
  private async sendOrderConfirmationEmail(notification: NotificationEventMap['send-order-confirmation']): Promise<void> {
    console.log(`📬 Sending order confirmation email for order: ${notification.orderId}`);
    // Email sending logic would go here
  }
  
  private async sendWelcomeEmail(welcome: NotificationEventMap['send-welcome-email']): Promise<void> {
    console.log(`📬 Sending welcome email to: ${welcome.email}`);
    // Welcome email logic would go here
  }
  
  // 📊 Get system metrics
  getSystemMetrics(): SystemMetrics {
    return this.eventBus.getMetrics();
  }
}

// 🏗️ Event map definitions for different domains
interface UserEventMap {
  'user-registered': {
    id: string;
    email: string;
    name: string;
    registeredAt: Date;
  };
  'user-login': {
    userId: string;
    loginAt: Date;
    ipAddress: string;
  };
  'user-logout': {
    userId: string;
    sessionDuration: number;
  };
}

interface OrderEventMap {
  'order-created': {
    id: string;
    userId: string;
    items: Array<{
      productId: string;
      quantity: number;
      price: number;
    }>;
    total: number;
    createdAt: Date;
  };
  'payment-processed': {
    orderId: string;
    amount: number;
    method: string;
    processedAt: Date;
  };
  'payment-failed': {
    orderId: string;
    amount: number;
    reason: string;
    failedAt: Date;
  };
}

interface InventoryEventMap {
  'reserve-items': {
    orderId: string;
    items: Array<{
      productId: string;
      quantity: number;
    }>;
  };
  'low-stock-alert': {
    productId: string;
    currentStock: number;
    threshold: number;
    alertedAt: Date;
  };
}

interface NotificationEventMap {
  'send-order-confirmation': {
    userId: string;
    orderId: string;
    total: number;
  };
  'send-welcome-email': {
    userId: string;
    email: string;
    name: string;
  };
  'send-payment-failure-notification': {
    orderId: string;
    reason: string;
  };
}

🎯 Performance & Best Practices

⚡ Optimizing Event Performance

Here are essential patterns for high-performance event systems:

// 🚀 High-performance event emitter with batching and throttling
class PerformantEventEmitter<TEventMap = Record<string, any>> extends AdvancedEventEmitter<TEventMap> {
  private batchConfig: Map<keyof TEventMap, BatchConfig> = new Map();
  private batchQueues: Map<keyof TEventMap, BatchQueue<any>> = new Map();
  private throttleConfig: Map<keyof TEventMap, ThrottleConfig> = new Map();
  private throttleState: Map<keyof TEventMap, ThrottleState> = new Map();
  
  // 📦 Configure event batching
  configureBatching<K extends keyof TEventMap>(
    event: K,
    config: BatchConfig
  ): this {
    console.log(`📦 Configuring batching for event: ${String(event)}`);
    this.batchConfig.set(event, config);
    this.batchQueues.set(event, {
      items: [],
      timer: null
    });
    return this;
  }
  
  // 🔄 Configure event throttling
  configureThrottling<K extends keyof TEventMap>(
    event: K,
    config: ThrottleConfig
  ): this {
    console.log(`🔄 Configuring throttling for event: ${String(event)}`);
    this.throttleConfig.set(event, config);
    this.throttleState.set(event, {
      lastEmitted: 0,
      pending: null
    });
    return this;
  }
  
  // 📤 Enhanced emit with batching and throttling
  async emit<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): Promise<void> {
    // 📦 Handle batching
    if (this.batchConfig.has(event)) {
      return this.handleBatchedEmit(event, payload);
    }
    
    // 🔄 Handle throttling
    if (this.throttleConfig.has(event)) {
      return this.handleThrottledEmit(event, payload);
    }
    
    // 📤 Regular emit
    return super.emit(event, payload);
  }
  
  // 📦 Handle batched event emission
  private async handleBatchedEmit<K extends keyof TEventMap>(
    event: K,
    payload: TEventMap[K]
  ): Promise<void> {
    const config = this.batchConfig.get(event)!;
    const queue = this.batchQueues.get(event)!;
    
    // 📦 Add to batch
    queue.items.push(payload);
    
    // 🚀 Process immediately if batch size reached
    if (queue.items.length >= config.size) {
      return this.processBatch(event);
    }
    
    // ⏰ Set timer for batch processing if not already set
    if (!queue.timer) {
      queue.timer = setTimeout(() => {
        this.processBatch(event);
      }, config.timeout);
    }
  }
  
  // 📦 Process batched events
  private async processBatch<K extends keyof TEventMap>(event: K): Promise<void> {
    const queue = this.batchQueues.get(event)!;
    
    if (queue.items.length === 0) return;
    
    const batchItems = [...queue.items];
    queue.items = [];
    
    // 🧹 Clear timer
    if (queue.timer) {
      clearTimeout(queue.timer);
      queue.timer = null;
    }
    
    console.log(`📦 Processing batch of ${batchItems.length} events for: ${String(event)}`);
    
    // 📤 Emit batch event
    await super.emit(event, batchItems as TEventMap[K]);
  }
  
  // 🔄 Handle throttled event emission
  private async handleThrottledEmit<K extends keyof TEventMap>(
    event: K,
    payload: TEventMap[K]
  ): Promise<void> {
    const config = this.throttleConfig.get(event)!;
    const state = this.throttleState.get(event)!;
    const now = Date.now();
    
    // ⚡ Check if we can emit immediately
    if (now - state.lastEmitted >= config.interval) {
      state.lastEmitted = now;
      return super.emit(event, payload);
    }
    
    // 🔄 Handle pending emission
    if (config.strategy === 'leading') {
      // 🔄 Ignore if already pending
      return;
    } else if (config.strategy === 'trailing') {
      // 🔄 Schedule trailing emission
      if (state.pending) {
        clearTimeout(state.pending);
      }
      
      const delay = config.interval - (now - state.lastEmitted);
      state.pending = setTimeout(async () => {
        state.lastEmitted = Date.now();
        state.pending = null;
        await super.emit(event, payload);
      }, delay);
    }
  }
  
  // 🧹 Cleanup method
  destroy(): void {
    // 🧹 Clear all batch timers
    for (const queue of this.batchQueues.values()) {
      if (queue.timer) {
        clearTimeout(queue.timer);
      }
    }
    
    // 🧹 Clear all throttle timers
    for (const state of this.throttleState.values()) {
      if (state.pending) {
        clearTimeout(state.pending);
      }
    }
    
    // 🧹 Clear all listeners
    this.removeAllListeners();
  }
}

// 🏗️ Performance configuration interfaces
interface BatchConfig {
  size: number;     // Maximum batch size
  timeout: number;  // Maximum wait time (ms)
}

interface ThrottleConfig {
  interval: number; // Throttle interval (ms)
  strategy: 'leading' | 'trailing'; // Throttle strategy
}

interface BatchQueue<T> {
  items: T[];
  timer: NodeJS.Timeout | null;
}

interface ThrottleState {
  lastEmitted: number;
  pending: NodeJS.Timeout | null;
}

// ⚡ Memory-efficient event emitter with weak references
class MemoryEfficientEventEmitter<TEventMap = Record<string, any>> extends TypeSafeEventEmitter<TEventMap> {
  private weakListeners: Map<keyof TEventMap, WeakSet<object>> = new Map();
  private listenerCallbacks: Map<object, Function> = new Map();
  
  // 👂 Add weak listener (automatically removed when object is garbage collected)
  onWeak<K extends keyof TEventMap>(
    event: K,
    listener: (payload: TEventMap[K]) => void | Promise<void>,
    target: object
  ): this {
    console.log(`👂 Adding weak listener for event: ${String(event)}`);
    
    if (!this.weakListeners.has(event)) {
      this.weakListeners.set(event, new WeakSet());
    }
    
    this.weakListeners.get(event)!.add(target);
    this.listenerCallbacks.set(target, listener);
    
    return this;
  }
  
  // 📤 Enhanced emit with weak reference support
  async emit<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): Promise<void> {
    // 📤 Emit to regular listeners
    await super.emit(event, payload);
    
    // 📤 Emit to weak listeners
    const weakSet = this.weakListeners.get(event);
    if (weakSet) {
      const promises: Promise<void>[] = [];
      
      for (const [target, callback] of this.listenerCallbacks) {
        if (weakSet.has(target)) {
          promises.push(this.executeListener(callback, payload));
        }
      }
      
      await Promise.all(promises);
    }
  }
  
  // 🧹 Cleanup orphaned weak references
  cleanupWeakReferences(): void {
    console.log('🧹 Cleaning up orphaned weak references...');
    
    // This would require additional tracking mechanism in real implementation
    // For demonstration purposes, we show the concept
  }
}

// 📊 Event metrics and monitoring
class MonitoredEventEmitter<TEventMap = Record<string, any>> extends PerformantEventEmitter<TEventMap> {
  private metrics: EventMetrics = {
    totalEvents: 0,
    eventsPerSecond: 0,
    averageListenerCount: 0,
    errorRate: 0,
    memoryUsage: 0
  };
  
  private metricsInterval: NodeJS.Timeout;
  private eventCounts: number[] = [];
  private errorCounts: number[] = [];
  
  constructor() {
    super();
    this.startMetricsCollection();
  }
  
  // 📤 Monitored emit with metrics collection
  async emit<K extends keyof TEventMap>(event: K, payload: TEventMap[K]): Promise<void> {
    const startTime = process.hrtime.bigint();
    
    try {
      await super.emit(event, payload);
      this.metrics.totalEvents++;
    } catch (error) {
      this.errorCounts.push(1);
      throw error;
    } finally {
      const endTime = process.hrtime.bigint();
      const duration = Number(endTime - startTime) / 1000000; // Convert to ms
      this.recordEventDuration(String(event), duration);
    }
  }
  
  // 📊 Start metrics collection
  private startMetricsCollection(): void {
    this.metricsInterval = setInterval(() => {
      this.updateMetrics();
    }, 1000); // Update metrics every second
  }
  
  // 📊 Update performance metrics
  private updateMetrics(): void {
    // 📈 Calculate events per second
    this.eventCounts.push(this.metrics.totalEvents);
    if (this.eventCounts.length > 60) { // Keep last 60 seconds
      this.eventCounts.shift();
    }
    
    if (this.eventCounts.length >= 2) {
      const recentEvents = this.eventCounts[this.eventCounts.length - 1] - this.eventCounts[this.eventCounts.length - 2];
      this.metrics.eventsPerSecond = recentEvents;
    }
    
    // 📊 Calculate error rate
    const totalErrors = this.errorCounts.reduce((sum, count) => sum + count, 0);
    this.metrics.errorRate = this.metrics.totalEvents > 0 ? totalErrors / this.metrics.totalEvents : 0;
    
    // 💾 Estimate memory usage
    this.metrics.memoryUsage = process.memoryUsage().heapUsed;
  }
  
  // ⏱️ Record event duration
  private recordEventDuration(event: string, duration: number): void {
    // In a real implementation, this would store duration metrics
    // for performance analysis and optimization
  }
  
  // 📊 Get current metrics
  getMetrics(): EventMetrics {
    return { ...this.metrics };
  }
  
  // 🧹 Cleanup
  destroy(): void {
    if (this.metricsInterval) {
      clearInterval(this.metricsInterval);
    }
    super.destroy();
  }
}

// 🏗️ Metrics interface
interface EventMetrics {
  totalEvents: number;
  eventsPerSecond: number;
  averageListenerCount: number;
  errorRate: number;
  memoryUsage: number;
}

🏆 Summary

Congratulations! 🎉 You’ve mastered event emitters and type-safe event-driven programming in TypeScript! Here’s what you’ve accomplished:

🎯 Key Takeaways

  1. Type Safety 🛡️: Build event systems with compile-time guarantees for event names and payloads
  2. Scalable Architecture 🏗️: Create loosely-coupled systems that grow with your application
  3. Performance Optimization 🚀: Implement batching, throttling, and memory-efficient patterns
  4. Enterprise Patterns 🏢: Build production-ready event systems with routing and namespacing
  5. Real-World Applications 🌐: Create complex business logic with elegant event flows

🛠️ What You Can Build

  • Microservice Communication 📡: Type-safe inter-service messaging
  • Real-Time Applications ⚡: Live dashboards and collaborative tools
  • Event-Driven Architectures 🏗️: Scalable business process automation
  • User Interface Systems 🖼️: Reactive component communication
  • IoT Data Processing 📊: Device event aggregation and processing

🚀 Next Steps

Ready to take your event-driven skills even further? Consider exploring:

  • WebSocket Integration 🌐: Real-time client-server communication
  • Message Queues 📬: Distributed event processing with Redis/RabbitMQ
  • Event Sourcing 📜: Store application state as sequence of events
  • CQRS Patterns 🔄: Command Query Responsibility Segregation

You’re now equipped to build sophisticated, scalable event-driven applications that handle complex business logic with elegance and type safety! 🎯 Keep emitting and listening! 📡✨