Real-time Communication
⚡ Real-time Communication
WebSocket Messages Arrive as Raw Strings
A raw ws message handler gives you a string (or a Buffer) — parsing it is on you, and nothing stops a malformed or unexpected message from crashing a handler:
✉️ A Typed Message Envelope
The same discriminated-union pattern from Chapter 3's ApiResponse<T> works just as well for socket messages — a type field the compiler can narrow on:
Just like Chapter 3's request bodies, the envelope's type is not enough on its own — it still has to be validated at runtime before it's trusted (see the gotcha below).
Validating Messages at the Socket Boundary
A Type-Safe Message Router
📢 Type-Safe Pub/Sub
A plain EventEmitter lets you emit and listen for any string event with any payload — nothing stops a typo or a mismatched payload shape. A generic wrapper fixes that:
Event Map Interface
One interface lists every event name and its exact payload shape — the single source of truth for what can be emitted or listened for.
Generic on / emit
K extends keyof Events ties the event name to its payload type, so mismatches are compile errors, not silent no-ops.
Event Streaming at Scale
A TypedEventBus works well inside one process. Across services, tools like Kafka or Redis Streams take over — the typing principle stays identical, just applied to producers and consumers instead of emit/on:
💻 Coding Challenges
Challenge 1: Design a Message Envelope
Write a ClientMessage discriminated union for a simple game ("move", "chat", "leave"), plus a handleMessage function with a switch that narrows correctly on each variant.
Goal: Practice discriminated unions applied to real-time message shapes.
Challenge 2: Validate Incoming Socket Data
Write a validator (zod schema or hand-rolled type guard) for your ClientMessage union from Challenge 1, and a message handler that rejects anything that doesn't match with an "error" response.
Goal: Practice never trusting JSON.parse() output without validating it first.
Challenge 3: Build a Typed Event Bus
Implement TypedEventBus<Events> with generic on/emit methods, define an AppEvents interface with at least three events, and demonstrate that a mismatched payload fails to compile.
Goal: Practice using a generic class constrained by keyof to keep pub/sub fully type-safe.
JSON.parse(raw) as ClientMessage compiles cleanly and proves nothing — a malicious or buggy client can send any JSON shape it wants, and as will not stop it. Every message boundary (WebSocket, Kafka consumer, any external event source) needs the same runtime validation Chapter 3 applied to HTTP request bodies. The type only describes what you expect; the validator confirms what you got.
🎯 What's Next
With messages flowing safely in real time, the next chapter turns to speed itself: Performance & Profiling — finding memory leaks, profiling CPU usage, and identifying bottlenecks in a running TypeScript application.