Bird
Raised Fist0
Typescriptprogramming~20 mins

Type-safe event emitter pattern in Typescript - Practice Problems & Coding Challenges

Choose your learning style10 modes available
Challenge - 5 Problems
🎖️
Type-safe Event Emitter Master
Get all challenges correct to earn this badge!
Test your skills under time pressure!
❓ Predict Output
intermediate
2:00remaining
What is the output of this type-safe event emitter example?

Consider this TypeScript code using a type-safe event emitter pattern. What will be printed when the code runs?

Typescript
type Events = {
  login: (user: string) => void;
  logout: () => void;
};

class EventEmitter<E> {
  private listeners: { [K in keyof E]?: E[K][] } = {};

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

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

const emitter = new EventEmitter<Events>();
emitter.on('login', user => console.log(`User logged in: ${user}`));
emitter.on('logout', () => console.log('User logged out'));

emitter.emit('login', 'Alice');
emitter.emit('logout');
AUser logged in: Alice
B
User logged in: Alice
User logged out
CUser logged out
DNo output
Attempts:
2 left
💡 Hint

Check how many listeners are registered and what events are emitted.

🧠 Conceptual
intermediate
1:30remaining
Which TypeScript feature ensures type safety in this event emitter?

In a type-safe event emitter pattern, which TypeScript feature is primarily responsible for ensuring that event names and their listener argument types match correctly?

AGenerics with mapped types
BType assertions
CType aliases without generics
DUnion types
Attempts:
2 left
💡 Hint

Think about how the event names and listener types are linked in the class definition.

🔧 Debug
advanced
2:00remaining
What error does this broken type-safe event emitter code produce?

Identify the error produced by this TypeScript code snippet:

type Events = {
  data: (value: number) => void;
};

class EventEmitter {
  private listeners: { [K in keyof E]?: E[K][] } = {};

  on(eventName: K, listener: E[K]) {
    this.listeners[eventName].push(listener);
  }

  emit(eventName: K, ...args: Parameters) {
    this.listeners[eventName]?.forEach(listener => listener(...args));
  }
}
ATypeError: listener is not a function
BSyntaxError: Unexpected token '.'
CTypeError: Cannot read property 'push' of undefined
DNo error
Attempts:
2 left
💡 Hint

Check what happens if no listeners are registered yet.

📝 Syntax
advanced
1:30remaining
Which option correctly types the emit method in a type-safe event emitter?

Choose the correct TypeScript signature for the emit method to ensure type safety for event arguments.

Aemit<K extends keyof E>(eventName: K, ...args: Parameters<E[K]>): void
Bemit(eventName: keyof E, args: any[]): void
Cemit(eventName: string, ...args: unknown[]): void
Demit<K>(eventName: K, ...args: any[]): void
Attempts:
2 left
💡 Hint

Look for the option that uses generics and extracts parameter types from the listener function.

🚀 Application
expert
2:30remaining
How many listeners are registered after this code runs?

Given this code snippet, how many listeners are registered for the 'update' event after execution?

type Events = {
  update: (data: string) => void;
};

class EventEmitter {
  private listeners: { [K in keyof E]?: E[K][] } = {};

  on(eventName: K, listener: E[K]) {
    if (!this.listeners[eventName]) {
      this.listeners[eventName] = [];
    }
    this.listeners[eventName]!.push(listener);
  }

  off(eventName: K, listener: E[K]) {
    this.listeners[eventName] = this.listeners[eventName]?.filter(l => l !== listener);
  }
}

const emitter = new EventEmitter();

function listener1(data: string) { console.log('Listener 1:', data); }
function listener2(data: string) { console.log('Listener 2:', data); }

emitter.on('update', listener1);
emitter.on('update', listener2);
emitter.off('update', listener1);
emitter.on('update', listener1);
A0
B1
C3
D2
Attempts:
2 left
💡 Hint

Count how many times listeners are added and removed.

Practice

(1/5)
1. What is the main benefit of using a type-safe event emitter in TypeScript?
easy
A. It makes the code run faster by optimizing event handling.
B. It ensures events and their data match expected types, preventing runtime errors.
C. It automatically logs all emitted events to the console.
D. It allows emitting events without defining their types.

Solution

  1. Step 1: Understand type safety in event emitters

    Type-safe event emitters use TypeScript types to ensure events and their data match expected types.
  2. Step 2: Identify the benefit of type safety

    This prevents runtime errors by catching mismatches during development, improving code reliability.
  3. Final Answer:

    It ensures events and their data match expected types, preventing runtime errors. -> Option B
  4. Quick Check:

    Type safety = prevents runtime errors [OK]
Hint: Remember: type-safe means matching event names and data types [OK]
Common Mistakes:
  • Thinking it improves runtime speed
  • Assuming it logs events automatically
  • Believing it allows untyped events
2. Which of the following is the correct way to define an interface for a type-safe event emitter with events login (payload: string) and logout (no payload)?
easy
A. interface Events { login: string; logout: null; }
B. interface Events { login: string; logout: void; }
C. interface Events { login: string; logout: undefined; }
D. interface Events { login: string; logout: never; }

Solution

  1. Step 1: Understand event payload types

    Events with no payload should use undefined to indicate no data is expected.
  2. Step 2: Check interface options

    interface Events { login: string; logout: undefined; } uses logout: undefined, which correctly represents no payload for the logout event.
  3. Final Answer:

    interface Events { login: string; logout: undefined; } -> Option C
  4. Quick Check:

    No payload event = undefined type [OK]
Hint: Use 'undefined' type for events with no payload [OK]
Common Mistakes:
  • Using void which is a function return type
  • Using null which means absence of value
  • Using never which means impossible type
3. Given this type-safe event emitter interface and usage:
interface Events {
  message: string;
  error: { code: number; message: string };
}

const emitter = new TypedEmitter<Events>();
emitter.on('message', (msg) => console.log(msg));
emitter.emit('error', { code: 404, message: 'Not found' });

What will be the output when the error event is emitted?
medium
A. No output because no listener for 'error' event is registered.
B. Logs: { code: 404, message: 'Not found' }
C. Throws a type error at runtime.
D. Logs: 'Not found'

Solution

  1. Step 1: Check registered event listeners

    Only a listener for 'message' event is registered, no listener for 'error'.
  2. Step 2: Understand event emission behavior

    Emitting 'error' without a listener produces no output; no automatic logging occurs.
  3. Final Answer:

    No output because no listener for 'error' event is registered. -> Option A
  4. Quick Check:

    Emit without listener = no output [OK]
Hint: Only events with listeners produce output [OK]
Common Mistakes:
  • Assuming emit logs automatically
  • Expecting runtime error without listener
  • Confusing event data with console output
4. Consider this code snippet using a type-safe event emitter:
interface Events {
  update: number;
}

const emitter = new TypedEmitter<Events>();
emitter.on('update', (data: string) => {
  console.log(data);
});

What is the problem with this code?
medium
A. There is no problem; the code is correct.
B. The event name 'update' is not defined in the interface.
C. The emitter instance is not created correctly.
D. The listener callback parameter type does not match the event payload type.

Solution

  1. Step 1: Check event payload type in interface

    The 'update' event expects a number payload.
  2. Step 2: Check listener callback parameter type

    The callback expects a string, which mismatches the expected number.
  3. Final Answer:

    The listener callback parameter type does not match the event payload type. -> Option D
  4. Quick Check:

    Listener param type must match event payload type [OK]
Hint: Match listener parameter type to event payload type [OK]
Common Mistakes:
  • Ignoring type mismatch warnings
  • Assuming any type is allowed
  • Confusing event names with payload types
5. You want to create a type-safe event emitter for a chat app with events:
- join: payload is username (string)
- leave: payload is username (string)
- message: payload is an object with user (string) and text (string)

Which interface correctly defines these events for type safety?
hard
A. interface ChatEvents { join: string; leave: string; message: { user: string; text: string }; }
B. interface ChatEvents { join: string; leave: string; message: string; }
C. interface ChatEvents { join: { username: string }; leave: { username: string }; message: { user: string; text: string }; }
D. interface ChatEvents { join: string; leave: undefined; message: { user: string; text: string }; }

Solution

  1. Step 1: Analyze event payload requirements

    'join' and 'leave' expect a username string; 'message' expects an object with user and text strings.
  2. Step 2: Match interface to requirements

    interface ChatEvents { join: string; leave: string; message: { user: string; text: string }; } matches exactly: 'join' and 'leave' as string, 'message' as object with user and text.
  3. Final Answer:

    interface ChatEvents { join: string; leave: string; message: { user: string; text: string }; } -> Option A
  4. Quick Check:

    Match payload types exactly for type safety [OK]
Hint: Match event names and payload types exactly in interface [OK]
Common Mistakes:
  • Using wrong payload types for events
  • Omitting required object structure
  • Using undefined for events that need payload
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard