← Alle AufgabenTypeScript · Middle

Typisierter EventEmitter

Die Klasse Emitter<Events> bekommt eine Beschreibung ihrer Events: new Emitter<{ login: { userId: number }; message: string }>(). on(event, handler) meldet einen Handler an und liefert eine Funktion zum Abmelden, emit(event, payload) ruft die Handler dieses Events auf.

Mach die Typen genau: ein Eventname kann nur einer der Schlüssel von Events sein, und der Typ von payload im Handler und in emit kommt von genau diesem Event.

Tests (7)

Typtests prüft der TypeScript-Compiler im strict-Modus. Expect<Equal<A, B>> besteht, wenn die Typen exakt gleich sind, und // @ts-expect-error, wenn die nächste Zeile nicht kompiliert. Der erste Start lädt den Compiler und dauert daher ein paar Sekunden.

  1. Typen
    type E = { login: { userId: number }; message: string };
    const e = new Emitter<E>();
    e.on('login', (payload) => {
      type _ = Expect<Equal<typeof payload, { userId: number }>>;
    });
  2. Typen
    type E = { login: { userId: number }; message: string };
    const e = new Emitter<E>();
    // @ts-expect-error - message carries a string
    e.emit('message', 42);
  3. Typen
    type E = { login: { userId: number }; message: string };
    const e = new Emitter<E>();
    // @ts-expect-error - there is no such event
    e.on('signup', () => {});
  4. const e = new Emitter();
    const a = fn();
    const b = fn();
    e.on('message', a);
    e.on('message', b);
    e.emit('message', 'hi');
    expect(a).toHaveBeenCalledWith('hi');
    expect(b).toHaveBeenCalledWith('hi');
  5. const e = new Emitter();
    const h = fn();
    const off = e.on('login', h);
    e.emit('login', { userId: 1 });
    off();
    e.emit('login', { userId: 2 });
    expect(h).toHaveBeenCalledTimes(1);
    expect(h).toHaveBeenLastCalledWith({ userId: 1 });
  6. const e = new Emitter();
    expect(() => e.emit('nobody-listens', 1)).not.toThrow();
  7. const e = new Emitter();
    const a = fn();
    const b = fn();
    e.on('x', a);
    e.on('y', b);
    e.emit('x', 1);
    expect(a).toHaveBeenCalledTimes(1);
    expect(b).toHaveBeenCalledTimes(0);