-
Notifications
You must be signed in to change notification settings - Fork 3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Emitting an event with acknowledgement and timeout breaks the Typescript client-to-server event definitions #1555
Comments
I could indeed reproduce the issue, thanks for the heads-up. Any idea on how one could fix this? |
I actually don't see a way to fix it without some breaking change, such as making the "error" parameter always present, or at least last so it can be made optional. I'm not sure though, there might be some clever typescript tweak to make it work. Btw a similar problem exists on the server, I'm not sure if I should raise a new issue or put it here: interface ServerToClientEvents{
"withAck": (data: { argName: boolean }, callback: (params: any) => void) => void;
}
const namespacedIO: Namespace<ClientToServerEvents, ServerToClientEvents> = io.of("/some-namespace");
namespacedIO.to("some-room").timeout(5000).emit("withAck", {
argNameWithTypo: true // due to the use of .timeout() Typescript no longer checks the arguments here, so you can make a typo in argName and it will still compile. If .timeout() is removed, then the compilation will fail, telling you argNameWithTypo is wrong.
}, (err: any, data: any) => {}); |
I think the following should work: type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args;
interface ClientToServerEvents<isEmitter extends boolean = false> {
withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void;
}
interface ServerToClientEvents<isEmitter extends boolean = false> {
}
const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000);
io.on("connection", (socket) => {
socket.on("withAck", (val, cb) => {
cb("123");
});
});
const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000");
socket.timeout(100).emit("withAck", { argName: true }, (err, val) => {
// ...
}); Not sure whether we can/should include this in the library though. |
Typed events were not applied when calling "io.timeout(...).emit()". Related: socketio/socket.io-client#1555 (comment) Reference: https://socket.io/docs/v4/typescript/
Added in the documentation there: https://socket.io/docs/v4/typescript/#emitting-with-a-timeout |
That worked, thanks! However I notice one small thing: // server-side
socket.to("some-room").timeout(5000).emit("serverEvent", "some-param", (err, dataParam) => {
// here dataParam is sent as an array, because there are multiple ACKs - one for each member of the room
});
// client-side
socket.timeout(5000).emit("clientEvent", "some-param", (err, dataParam) => {
// here dataParam is not an array, because there's only one ACK - the server
});
socket.on("serverEvent", (someParam, callback) => {
callback("data-param"); // if we define a string here, then the dataParam in the server will be a string, instead of the correct string[]
}); So I modified that helper a bit to introduce a new parameter - isServer - must be true in server-side ServerToClient interface and false in all other cases: type WithTimeoutAck<isServer extends boolean, isSender extends boolean, args extends any[]> =
isSender extends true
? isServer extends true
? [Error, [...args]]
: [Error, ...args]
: isServer extends true
? [args]
: args; |
Something like below maybe? @darrachequesne
|
When emitting with a timeout (added in version 4.4.0), the "err" argument was not properly typed and would require to split the client and server typings. It will now be automatically inferred as an Error object. Workaround for previous versions: ```ts type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args; interface ClientToServerEvents<isEmitter extends boolean = false> { withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void; } interface ServerToClientEvents<isEmitter extends boolean = false> { } const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000); io.on("connection", (socket) => { socket.on("withAck", (val, cb) => { cb("123"); }); }); const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000"); socket.timeout(100).emit("withAck", { argName: true }, (err, val) => { // ... }); ``` Related: #1555
When emitting with a timeout (added in version 4.4.0), the "err" argument was not properly typed and would require to split the client and server typings. It will now be automatically inferred as an Error object. Workaround for previous versions: ```ts type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args; interface ClientToServerEvents<isEmitter extends boolean = false> { withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void; } interface ServerToClientEvents<isEmitter extends boolean = false> { } const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000); io.on("connection", (socket) => { socket.on("withAck", (val, cb) => { cb("123"); }); }); const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000"); socket.timeout(100).emit("withAck", { argName: true }, (err, val) => { // ... }); ``` Related: socketio/socket.io-client#1555
Typed events were not applied when calling "io.timeout(...).emit()". Related: socketio/socket.io-client#1555 (comment) Reference: https://socket.io/docs/v4/typescript/
When emitting with a timeout (added in version 4.4.0), the "err" argument was not properly typed and would require to split the client and server typings. It will now be automatically inferred as an Error object. Workaround for previous versions: ```ts type WithTimeoutAck<isEmitter extends boolean, args extends any[]> = isEmitter extends true ? [Error, ...args] : args; interface ClientToServerEvents<isEmitter extends boolean = false> { withAck: (data: { argName: boolean }, callback: (...args: WithTimeoutAck<isEmitter, [string]>) => void) => void; } interface ServerToClientEvents<isEmitter extends boolean = false> { } const io = new Server<ClientToServerEvents, ServerToClientEvents<true>>(3000); io.on("connection", (socket) => { socket.on("withAck", (val, cb) => { cb("123"); }); }); const socket: Socket<ServerToClientEvents, ClientToServerEvents<true>> = ioc("http://localhost:3000"); socket.timeout(100).emit("withAck", { argName: true }, (err, val) => { // ... }); ``` Related: socketio/socket.io-client#1555
Describe the bug
Emitting an event using a timeout and acknowledgement causes the acknowledgement function to change its arguments list, so that Error is first argument. This breaks the Typescript definitions. See below for more details:
To Reproduce
Socket.IO client version:
4.5.2
Client
Expected behavior
It should be possible to define one ClientToServerEvents interface and use it both with client and server, irrespective if events are emitted with timeout or not.
The text was updated successfully, but these errors were encountered: