Real-time App with Socket.io
In this page:
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
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
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
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
// 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
type EventName = "message" | "join" | "leave";
function emit(event: EventName, payload: unknown) {
console.log(event, payload);
}
emit("message", { text: "hi" });
Chapter Quiz — Complete all 14 topics to unlock
0/14 topics done
Complete these topics first:
- Todo App with TypeScript
- REST API with Express + TypeScript
- React Dashboard with TypeScript
- CLI Tool with TypeScript
- Library with TypeScript
- Full Stack TypeScript App
- TypeScript Design System
- TypeScript Monorepo Project
- Authentication System
- Real-time App with Socket.io
- GraphQL API with TypeScript
- Microservices with TypeScript
- TypeScript Best Practices Review
- What to Learn Next