📖 Scenario: You are building a simple event system for a chat app. Different parts of the app listen to events like new messages or user status changes. You want to make sure the events and their data are always the right type to avoid bugs.
🎯 Goal: Create a type-safe event emitter in TypeScript that only allows specific events with correct data types. Then use it to listen and emit events safely.
📋 What You'll Learn
Create an interface called ChatEvents with two events: message and userStatus
Define message event data as an object with from (string) and text (string)
Define userStatus event data as an object with user (string) and online (boolean)
Create a generic EventEmitter class that uses the ChatEvents interface for type safety
Add on and emit methods to EventEmitter that enforce event names and data types
Use the EventEmitter to listen for message and userStatus events and print their data
💡 Why This Matters
🌍 Real World
Event emitters are used in apps to handle things like user actions, data updates, or notifications in a clean way.
💼 Career
Understanding type-safe event emitters helps you build reliable, maintainable code in TypeScript, a skill valued in frontend and backend development.
Progress0 / 4 steps
1
Define the event types interface
Create an interface called ChatEvents with two properties: message and userStatus. Set message to an object with from (string) and text (string). Set userStatus to an object with user (string) and online (boolean).
Typescript
Hint
Use interface keyword and define the two event names as keys with their object types.
2
Create the generic EventEmitter class
Create a generic class called EventEmitter with a type parameter T. Inside, create a private property listeners as an object mapping keys of T to arrays of functions that take the event data and return void. Initialize listeners as an empty object.
Typescript
Hint
Use a mapped type for listeners to store arrays of callback functions for each event key.
3
Add on and emit methods with type safety
Inside EventEmitter, add a method on that takes an event name event of type keyof T and a callback listener that takes data of type T[typeof event]. The method should add the listener to the listeners array for that event. Also add a method emit that takes an event name and data of the correct type, and calls all listeners for that event with the data.
Typescript
Hint
Use generic methods with K extends keyof T to keep event names and data types linked.
4
Use EventEmitter to listen and emit events
Create an instance chatEmitter of EventEmitter using ChatEvents. Use chatEmitter.on to listen for message events and print Message from {from}: {text}. Also listen for userStatus events and print User {user} is {online ? 'online' : 'offline'}. Then emit a message event with from as 'Alice' and text as 'Hello!'. Emit a userStatus event with user as 'Bob' and online as true.
Typescript
Hint
Use console.log inside listeners to print the event data. Emit events with correct data objects.
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
Step 1: Understand type safety in event emitters
Type-safe event emitters use TypeScript types to ensure events and their data match expected types.
Step 2: Identify the benefit of type safety
This prevents runtime errors by catching mismatches during development, improving code reliability.
Final Answer:
It ensures events and their data match expected types, preventing runtime errors. -> Option B
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
Step 1: Understand event payload types
Events with no payload should use undefined to indicate no data is expected.
Step 2: Check interface options
interface Events { login: string; logout: undefined; } uses logout: undefined, which correctly represents no payload for the logout event.
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
Step 1: Check event payload type in interface
The 'update' event expects a number payload.
Step 2: Check listener callback parameter type
The callback expects a string, which mismatches the expected number.
Final Answer:
The listener callback parameter type does not match the event payload type. -> Option D
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?
'join' and 'leave' expect a username string; 'message' expects an object with user and text strings.
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.