← run

ts-04-event-emitter

1.000
8/8 tests· typing

Proposed solution

```typescript
export class EventEmitter<Events extends Record<string, unknown[]>> {
  private listeners: Map<keyof Events, Array<(...args: any[]) => void>> = new Map();

  on<E extends keyof Events>(event: E, fn: (...args: Events[E]) => void): () => void {
    if (!this.listeners.has(event)) {
      this.listeners.set(event, []);
    }
    const eventListeners = this.listeners.get(event)!;
    eventListeners.push(fn);
    
    return () => {
      const index = eventListeners.indexOf(fn);
      if (index !== -1) {
        eventListeners.splice(index, 1);
      }
    };
  }

  off<E extends keyof Events>(event: E, fn: (...args: Events[E]) => void): void {
    if (!this.listeners.has(event)) {
      return;
    }
    const eventListeners = this.listeners.get(event)!;
    const index = eventListeners.indexOf(fn);
    if (index !== -1) {
      eventListeners.splice(index, 1);
    }
  }

  emit<E extends keyof Events>(event: E, ...args: Events[E]): void {
    if (!this.listeners.has(event)) {
      return;
    }
    const eventListeners = this.listeners.get(event)!;
    for (const listener of eventListeners) {
      listener(...args);
    }
  }
}
```

Test output (stdout)

TAP version 13
# Subtest: calls a registered listener with args
ok 1 - calls a registered listener with args
  ---
  duration_ms: 1.060626
  type: 'test'
  ...
# Subtest: multiple listeners fire in registration order
ok 2 - multiple listeners fire in registration order
  ---
  duration_ms: 0.081161
  type: 'test'
  ...
# Subtest: passes multiple typed args
ok 3 - passes multiple typed args
  ---
  duration_ms: 0.05468
  type: 'test'
  ...
# Subtest: returned unsubscribe removes the listener
ok 4 - returned unsubscribe removes the listener
  ---
  duration_ms: 0.05146
  type: 'test'
  ...
# Subtest: off removes a specific listener, leaving others
ok 5 - off removes a specific listener, leaving others
  ---
  duration_ms: 0.076051
  type: 'test'
  ...
# Subtest: emitting an event with no listeners is a no-op
ok 6 - emitting an event with no listeners is a no-op
  ---
  duration_ms: 0.07677
  type: 'test'
  ...
# Subtest: off on an unregistered listener is a no-op
ok 7 - off on an unregistered listener is a no-op
  ---
  duration_ms: 0.04146
  type: 'test'
  ...
# Subtest: the same function registered twice fires twice
ok 8 - the same function registered twice fires twice
  ---
  duration_ms: 0.06751
  type: 'test'
  ...
1..8
# tests 8
# suites 0
# pass 8
# fail 0
# cancelled 0
# skipped 0
# todo 0
# duration_ms 80.553033
System prompt
You are an expert programmer. Solve the task exactly as specified. Output your solution as fenced code blocks using the required file name(s) and the exact function/type signatures requested. Prefer correctness; do not include prose outside code unless asked.