Skip to main content
Latest blog post (July 25, 2024): npm package provenance.
Version: 4.x

TypeScript

Starting with v3, Socket.IO now has first class support for TypeScript.

Types for the server

First, declare some types:

interfaceServerToClientEvents{
noArg:()=>void;
basicEmit:(a:number, b:string, c: Buffer)=>void;
withAck:(d:string,callback:(e:number)=>void)=>void;
}

interfaceClientToServerEvents{
hello:()=>void;
}

interfaceInterServerEvents{
ping:()=>void;
}

interfaceSocketData{
name:string;
age:number;
}

And use them when creating your server:

const io =newServer<
ClientToServerEvents,
ServerToClientEvents,
InterServerEvents,
SocketData
>();

Then, profit from the help of your IDE!

The events declared in the ServerToClientEvents interface are used when sending and broadcasting events:

io.on("connection",(socket)=>{
socket.emit("noArg");
socket.emit("basicEmit",1,"2", Buffer.from([3]));
socket.emit("withAck","4",(e)=>{
// e is inferred as number
});

// works when broadcast to all
io.emit("noArg");

// works when broadcasting to a room
io.to("room1").emit("basicEmit",1,"2", Buffer.from([3]));
});

The ones declared in the ClientToServerEvents interface are used when receiving events:

io.on("connection",(socket)=>{
socket.on("hello",()=>{
// ...
});
});
tip

This also works with the Promise-based API:

interfaceServerToClientEvents{
// [...]
withAck:(d:string,callback:(result:number)=>void)=>void;
}

try{
const result =await socket.timeout(5_000).emitWithAck("withAck","some string");
// result is inferred as <number>
}catch(err){
// handle error
}

The events declared in the InterServerEvents interface are used for inter-server communication (added in socket.io@4.1.0):

io.serverSideEmit("ping");

io.on("ping",()=>{
// ...
});

And finally, the SocketData type is used to type the socket.data attribute (added in socket.io@4.4.0):

io.on("connection",(socket)=>{
socket.data.name ="john";
socket.data.age =42;
});
caution

These type hints do not replace proper validation/sanitization of the input. As usual, never trust user input.

Types for the client

On the client side, you can reuse the same ServerToClientEvents and ClientToServerEvents interfaces:

import{ io, Socket }from"socket.io-client";

// please note that the types are reversed
const socket: Socket<ServerToClientEvents, ClientToServerEvents>=io();

Similarly, the events declared in the ClientToServerEvents interface are used when sending events:

socket.emit("hello");

And the ones declared in ServerToClientEvents are used when receiving events:

socket.on("noArg",()=>{
// ...
});

socket.on("basicEmit",(a, b, c)=>{
// a is inferred as number, b as string and c as buffer
});

socket.on("withAck",(d, callback)=>{
// d is inferred as string and callback as a function that takes a number as argument
});

Custom types for each namespace

Since each Namespace can have its own set of events, you can also provide some types for each one of them:

import{ Server }from"socket.io";

// types for the main namespace
const io =newServer<ClientToServerEvents, ServerToClientEvents, InterServerEvents, SocketData>();

// types for the namespace named "/my-namespace"
interfaceNamespaceSpecificClientToServerEvents{
foo:(arg:string)=>void
}

interfaceNamespaceSpecificServerToClientEvents{
bar:(arg:string)=>void;
}

interfaceNamespaceSpecificInterServerEvents{
// ...
}

interfaceNamespaceSpecificSocketData{
// ...
}

const myNamespace: Namespace<
NamespaceSpecificClientToServerEvents,
NamespaceSpecificServerToClientEvents,
NamespaceSpecificInterServerEvents,
NamespaceSpecificSocketData
>= io.of("/my-namespace");

myNamespace.on("connection",(socket)=>{
socket.on("foo",()=>{
// ...
});

socket.emit("bar","123");
});

And on the client side:

import{ io, Socket }from"socket.io-client";

const socket: Socket<
NamespaceSpecificServerToClientEvents,
NamespaceSpecificClientToServerEvents
>=io("/my-namespace");

socket.on("bar",(arg)=>{
console.log(arg);// "123"
});

AltStyle によって変換されたページ (->オリジナル) /