← Back to TypeScript Course | Chapter 28: Real World Projects | Lesson 10 of 14

Real-time App with Socket.io

Socket.io can provide real-time communication between clients and servers. TypeScript can define event names and payload shapes shared by both sides.

Core Concept

Typing a Socket.IO app means declaring the exact shape of every event name and payload, so emitting an event with the wrong argument type — on either the client or server — is caught at compile time instead of failing silently at runtime.

Example: Core Concept

typescript
interface ServerToClientEvents {
  message: (payload: { user: string; text: string }) => void;
}
// socket.emit("message", { user: "Ravi", text: "hi" }) is now type-checked
console.log("Event names and payloads are typed, not stringly-typed");

Basic Setup

A basic setup uses Socket.IO's generic Server<ClientToServerEvents, ServerToClientEvents> and Socket types, defining each event as a method signature in a shared events interface.

Example: Basic Setup

typescript
interface ClientToServerEvents {
  join: (room: string) => void;
}
interface ServerToClientEvents {
  message: (payload: { user: string; text: string }) => void;
}
// const io: Server<ClientToServerEvents, ServerToClientEvents> = new Server();
console.log("Server<ClientToServerEvents, ServerToClientEvents> types every event");

Typed Example

A typed example: interface ServerToClientEvents { message: (payload: { user: string; text: string }) => void } means calling socket.emit(message, { user, text }) is checked against that exact payload shape, and a missing field is a compile error.

Example: Typed Example

typescript
interface ServerToClientEvents {
  message: (payload: { user: string; text: string }) => void;
}
function emitMessage(payload: { user: string; text: string }) {
  console.log("emit('message', ", payload, ")");
}
emitMessage({ user: "Ravi", text: "Hello" });

Project Usage

In a real project, sharing the events interface between the Node server and the browser client (via a shared-types package) keeps both sides in sync automatically whenever a new real-time event is added or changed.

Example: Project Usage

typescript
// shared/events.ts
export interface ServerToClientEvents {
  message: (payload: { user: string; text: string }) => void;
}
console.log("Shared events interface keeps server and client in sync");

Best Practices

Keep event names as a single shared union type used by both emit and listen calls, so renaming an event is a compile-time-checked change everywhere it's used instead of a string that has to be manually grepped for.

Example: Best Practices

typescript
type EventName = "message" | "join" | "leave";
function emit(event: EventName, payload: unknown) {
  console.log(event, payload);
}
emit("message", { text: "hi" });

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.