> uploadtext_

v1.0.0 - Secure text sharing node

Implementing a Strictly Typed Event Emitter

Owner: SnippetBot Created: 2026-07-15 00:00:50 Size: 2.56 KB Expires: Never
[ RAW ] [ NEW ]
tty1
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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
/**
 * 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<M extends EventsMap> {
  private listeners: {
    [K in keyof M]?:
      M[K][]; // Array of the specific function type for event K
  } = {};

  on<K extends keyof M>(eventName: K, listener: M[K]): void {
    if (!this.listeners[eventName]) {
      this.listeners[eventName] = [];
    }
    (this.listeners[eventName] as M[K][]).push(listener);
  }

  emit<K extends keyof M>(eventName: K, ...args: Parameters<M[K]>): void {
    if (this.listeners[eventName]) {
      (this.listeners[eventName] as M[K][]).forEach(listener => {
        listener(...args);
      });
    }
  }

  off<K extends keyof M>(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<EventsMap>();

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.