/** * Defines a strictly typed event emitter interface and a basic implementation. * The `EventsMap` generic parameter ensures that event names correspond * to their correct payload types, preventing runtime type errors. */ interface EventsMap { 'userLoggedIn': (userId: string) => void; 'productUpdated': (productId: string, newPrice: number) => void; 'appError': (error: Error) => void; } class TypedEventEmitter { private listeners: { [K in keyof M]?: M[K][]; // Array of the specific function type for event K } = {}; on(eventName: K, listener: M[K]): void { if (!this.listeners[eventName]) { this.listeners[eventName] = []; } (this.listeners[eventName] as M[K][]).push(listener); } emit(eventName: K, ...args: Parameters): void { if (this.listeners[eventName]) { (this.listeners[eventName] as M[K][]).forEach(listener => { listener(...args); }); } } off(eventName: K, listener: M[K]): void { if (!this.listeners[eventName]) return; const index = (this.listeners[eventName] as M[K][]).indexOf(listener); if (index > -1) { (this.listeners[eventName] as M[K][]).splice(index, 1); } } } // Example Usage: const appEvents = new TypedEventEmitter(); const userLoginListener = (userId: string) => { console.log(`User ${userId} logged in.`); }; const productUpdateListener = (productId: string, newPrice: number) => { console.log(`Product ${productId} updated to price: $${newPrice.toFixed(2)}.`); }; const errorListener = (error: Error) => { console.error(`Application error: ${error.message}`); }; appEvents.on('userLoggedIn', userLoginListener); appEvents.on('productUpdated', productUpdateListener); appEvents.on('appError', errorListener); appEvents.emit('userLoggedIn', 'jane.doe'); // OK appEvents.emit('productUpdated', 'PROD-789', 29.99); // OK appEvents.emit('appError', new Error('Something went wrong!')); // OK // appEvents.emit('userLoggedIn', 123); // Type Error: Argument of type 'number' is not assignable to parameter of type 'string'. // appEvents.emit('productUpdated', 'PROD-123'); // Type Error: Expected 2 arguments, but got 1. // appEvents.emit('unknownEvent', 'data'); // Type Error: Argument of type '"unknownEvent"' is not assignable to parameter of type 'keyof EventsMap'. appEvents.off('userLoggedIn', userLoginListener); console.log(' Emitting after removing a listener:'); appEvents.emit('userLoggedIn', 'john.doe'); // This will not log anything as the listener was removed.