Description
Describe the bug
When using emit
, emitWithAck
, and the server side variants, there are issues with the types of Broadcaster, Server, Namespace, and Socket types.
To Reproduce
Socket.IO server version: 4.7.2
Server
import { Server } from "socket.io";
export interface ServerToClientEvents {
basicEmit: (a: number, b: string) => void;
withAck: (d: string, callback: (e: number) => void) => void;
// multiple Ack arguments doesn't work on broadcasts - single target
withAck2: (d: string, callback: (e: number, f: string) => void) => void;
}
export interface ClientToServerEvents {}
const io = new Server<ClientToServerEvents,ServerToClientEvents>(3000, {});
//Emits
// no callback
io.timeout(1).emit('basicEmit',3,'')
// Likely should be a type error as it doesn't have a callback at all
// Currently just times out...
// One arg in callback
io.emit('withAck', 'hello', (...args) => { });
//=> ❌ typeof args === [number], should be [number[]]
io.timeout(1).emit('withAck', 'hello', (...args) => {});
//=> ✅ typeof args === [Error, number[]]
io.to('1').to('2').emit('withAck','',(...args)=>{})
//=> ❌ typeof args === [number], should be [number[]]
// Two args in callback
io.emit('withAck2', 'hello', (...args) => { });
//=> ❌ typeof args === [number,string], should be [number[]]
io.timeout(1).emit('withAck2','hello',(...args)=>{});
//=> ❌ typeof args === [Error,number,string], should be [Error,number[]]
// EmitWithAcks
// no callback
io.emitWithAck('basicEmit',3,'')
//=> type error: Expected 2 arguments, got 3 (the last argument is clipped off)
// Should just be a type-error, as without `timeout` applied, it will likely just never resolve
// if you apply a timeout, then it just times out eventually due to not having a callback
// One arg in callback
io.emitWithAck('withAck','hello');
//=>❌ type is Promise<any>, should be Promise<number[]>
io.timeout(1).emitWithAck('withAck','hello')
//=>✅ type is Promise<number[]>
// Two args in callback - doesn't even work in timeout broadcaster
io.emitWithAck('withAck2','hello');
//=>❌ type is Promise<any>, should be Promise<number[]>
io.timeout(1).emitWithAck('withAck2','hello')
//=>❌ type is Promise<any>, should be Promise<number[]>
io.on("connection", (socket) => {
// emit is fine on the socket instance itself
socket.broadcast.emit('withAck','hello',(...args)=>{});
//=>❌ typeof args === [number], should be [number[]]
socket.broadcast.timeout(1000).emit('withAck', 'hello',(...args)=>{});
//=>✅ typeof args === [Error, number[]]
// important to note that whatever types exist needs to work with socket.broadcast.timeout(1) AND socket.timeout(1).broadcast
socket.timeout(1).broadcast.timeout(1).emit('withAck','hello',(...args)=>{});
//=>❌ typeof args === [Error, Error, number[]], should be [Error, number[]]
// Needs to not re-decorate the event if an error was already added. (not sure what benefit allowing this gives in the first place
// emitWithAck is backwards here vs the broadcast based ones...
socket.emitWithAck('withAck','hello');
//=>✅ Promise<number>
socket.timeout(1).emitWithAck('withAck','hello');
//=>❌ Promise<any>, should be Promise<number>
// two args
socket.emitWithAck('withAck2','hello');
//=>❌ Promise<any>, should be Promise<number>
socket.timeout(1).emitWithAck('withAck2','hello');
//=>❌ Promise<any>, should be Promise<number>
});
Socket.IO client version: x.y.z
Client
I haven't checked on the client, but I assume the same types are used on here too (to some extent)
Expected behavior
Type definitions should be consistent with what socket.io is doing so that we can fully rely on them. As of the moment, they are wrong in a few places that are either frustrating (any
instead of a real type) or problematic (number
instead of number[]
).
Platform:
- Typescript 5.2
Additional context
I have some first passes at solving some of these issues that I can work towards improving and turning into a PR, if that's desired.