Skip to content
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

Fix: Calling io.emit("event") with events that have no arguments results in TypeScript errors #4915

Merged
merged 5 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions lib/typed-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,11 @@ export type EventNamesWithoutAck<
> = IfAny<
Last<Parameters<Map[K]>> | Map[K],
K,
K extends (
Last<Parameters<Map[K]>> extends (...args: any[]) => any ? never : K
)
K extends (Parameters<Map[K]> extends never[] ? K : never)
? K
: K extends (
Last<Parameters<Map[K]>> extends (...args: any[]) => any ? never : K
)
? K
: never
>;
Expand Down
4 changes: 2 additions & 2 deletions test/connection-state-recovery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -222,11 +222,11 @@ describe("connection state recovery", () => {

class DummyAdapter extends Adapter {
override persistSession(session) {
expect.fail();
expect().fail();
}

override restoreSession(pid, offset) {
expect.fail();
expect().fail();
return Promise.reject("should not happen");
}
}
Expand Down
2 changes: 1 addition & 1 deletion test/messaging-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ describe("messaging many", () => {
]).then(async () => {
try {
await io.timeout(200).emitWithAck("some event");
expect.fail();
expect().fail();
} catch (err) {
expect(err).to.be.an(Error);
// @ts-ignore
Expand Down
3 changes: 3 additions & 0 deletions test/namespaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
createClient,
successFn,
createPartialDone,
assert,
} from "./support/util";

describe("namespaces", () => {
Expand Down Expand Up @@ -417,6 +418,7 @@ describe("namespaces", () => {
socket1.on("a", successFn(done, io, socket1, socket2));

socket2.on("connect", () => {
assert(socket2.id);
io.except(socket2.id).emit("a");
});
});
Expand All @@ -435,6 +437,7 @@ describe("namespaces", () => {
socket1.on("a", successFn(done, io, socket1, socket2));

socket2.on("connect", () => {
assert(socket2.id);
nsp.except(socket2.id).emit("a");
});
});
Expand Down
2 changes: 1 addition & 1 deletion test/socket-timeout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ describe("timeout", () => {
io.on("connection", async (socket) => {
try {
await socket.timeout(50).emitWithAck("unknown");
expect.fail();
expect().fail();
} catch (err) {
expect(err).to.be.an(Error);
success(done, io, client);
Expand Down
43 changes: 41 additions & 2 deletions test/socket.io.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ describe("server", () => {
Ev extends keyof Map = keyof Map
> = (ev: Ev, ...args: Parameters<Map[Ev]>) => ReturnType<Map[Ev]>;
interface ClientToServerEvents {
noArgs: () => void;
helloFromClient: (message: string) => void;
ackFromClient: (
a: string,
Expand All @@ -196,6 +197,7 @@ describe("server", () => {
}

interface ServerToClientEvents {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
ackFromServer: (
a: boolean,
Expand All @@ -212,12 +214,14 @@ describe("server", () => {
// While these could be generated using the types from typed-events,
// it's likely better to just write them out, so that both the types and this are tested properly
interface ServerToClientEventsNoAck {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
ackFromServer: never;
ackFromServerSingleArg: never;
onlyCallback: never;
}
interface ServerToClientEventsWithError {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
ackFromServer: (
a: boolean,
Expand All @@ -233,6 +237,7 @@ describe("server", () => {
}

interface ServerToClientEventsWithMultiple {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
ackFromServer: (a: boolean, b: string, ack: (c: boolean[]) => void) => void;
ackFromServerSingleArg: (
Expand All @@ -243,6 +248,7 @@ describe("server", () => {
onlyCallback: (a: () => void) => void;
}
interface ServerToClientEventsWithMultipleAndError {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
ackFromServer: (
a: boolean,
Expand Down Expand Up @@ -279,7 +285,7 @@ describe("server", () => {
sio.send(1, "2", [3]);
// @ts-expect-error - ServerToClientEvents doesn't have a message event
nio.send(1, "2", [3]);
// This should likely be an error, but I don't know how to make it one
// This correctly becomes an error in TS 5.3.2, so when updating typescript, this should expect-error
sio.send();
nio.send();
});
Expand Down Expand Up @@ -383,6 +389,9 @@ describe("server", () => {
});
it("has the correct types for `emit`", () => {
const sio = new Server<ClientToServerEvents, ServerToClientEvents>();
expectType<ToEmit<ServerToClientEventsWithMultipleAndError, "noArgs">>(
sio.timeout(0).emit<"noArgs">
);
expectType<
ToEmit<ServerToClientEventsWithMultipleAndError, "helloFromServer">
>(sio.timeout(0).emit<"helloFromServer">);
Expand All @@ -406,6 +415,8 @@ describe("server", () => {
expectType<never>(
undefined as Parameters<typeof sansTimeout["emitWithAck"]>[0]
);
// @ts-expect-error - "noArgs" doesn't have a callback and is thus excluded
sio.timeout(0).emitWithAck("noArgs");
// @ts-expect-error - "helloFromServer" doesn't have a callback and is thus excluded
sio.timeout(0).emitWithAck("helloFromServer");
// @ts-expect-error - "onlyCallback" doesn't have a callback and is thus excluded
Expand All @@ -428,7 +439,12 @@ describe("server", () => {
it("Infers correct types", () => {
const sio = new Server<ClientToServerEvents, ServerToClientEvents>();
const nio = sio.of("/test");

expectType<ToEmit<ServerToClientEventsNoAck, "noArgs">>(
sio.emit<"noArgs">
);
expectType<ToEmit<ServerToClientEventsNoAck, "noArgs">>(
nio.emit<"noArgs">
);
expectType<ToEmit<ServerToClientEventsNoAck, "helloFromServer">>(
// These errors will dissapear once the TS version is updated from 4.7.4
// the TSD instance is using a newer version of TS than the workspace version
Expand All @@ -439,6 +455,7 @@ describe("server", () => {
nio.emit<"helloFromServer">
);
sio.on("connection", (s) => {
expectType<ToEmit<ServerToClientEvents, "noArgs">>(s.emit<"noArgs">);
expectType<ToEmit<ServerToClientEvents, "helloFromServer">>(
s.emit<"helloFromServer">
);
Expand Down Expand Up @@ -474,6 +491,8 @@ describe("server", () => {
it("Infers correct types", () => {
const sio = new Server<ClientToServerEvents, ServerToClientEvents>();
sio.on("connection", (s) => {
// @ts-expect-error - "noArgs" doesn't have a callback and is thus excluded
s.emitWithAck("noArgs");
// @ts-expect-error - "helloFromServer" doesn't have a callback and is thus excluded
s.emitWithAck("helloFromServer");
// @ts-expect-error - "onlyCallback" doesn't have a callback and is thus excluded
Expand Down Expand Up @@ -506,6 +525,10 @@ describe("server", () => {
srv.listen(() => {
sio.on("connection", (s) => {
expectType<Socket<ClientToServerEvents, ServerToClientEvents>>(s);
s.on("noArgs", (...args) => {
expectType<[]>(args);
done();
});
s.on("helloFromClient", (message) => {
expectType<string>(message);
done();
Expand All @@ -526,6 +549,10 @@ describe("server", () => {
const sio = new Server<ClientToServerEvents, ServerToClientEvents>(srv);
srv.listen(() => {
sio.on("connection", (s) => {
// @ts-expect-error - shouldn't accept emit events
s.on("noArgs", (message, number) => {
done();
});
// @ts-expect-error - shouldn't accept emit events
s.on("helloFromServer", (message, number) => {
done();
Expand All @@ -538,14 +565,17 @@ describe("server", () => {

describe("listen and emit event maps for the serverSideEmit method", () => {
interface ClientToServerEvents {
noArgs: () => void;
helloFromClient: (message: string) => void;
}

interface ServerToClientEvents {
noArgs: () => void;
helloFromServer: (message: string, x: number) => void;
}

interface InterServerEvents {
noArgs: () => void;
helloFromServerToServer: (message: string, x: number) => void;
ackFromServerToServer: (foo: string, cb: (bar: number) => void) => void;
}
Expand All @@ -563,20 +593,29 @@ describe("server", () => {
Server<ClientToServerEvents, ServerToClientEvents, InterServerEvents>
>(sio);
srv.listen(async () => {
sio.serverSideEmit("noArgs");
sio.serverSideEmit("helloFromServerToServer", "hello", 10);
sio
.of("/test")
.serverSideEmit("helloFromServerToServer", "hello", 10);

sio.on("noArgs", (...args) => {
expectType<[]>(args);
});
sio.on("helloFromServerToServer", (message, x) => {
expectType<string>(message);
expectType<number>(x);
});
sio.of("/test").on("noArgs", (...args) => {
expectType<[]>(args);
});
sio.of("/test").on("helloFromServerToServer", (message, x) => {
expectType<string>(message);
expectType<number>(x);
});

//@ts-expect-error - "helloFromServerToServer" does not have a callback
sio.serverSideEmitWithAck("noArgs");
//@ts-expect-error - "helloFromServerToServer" does not have a callback
sio.serverSideEmitWithAck("helloFromServerToServer", "hello");

Expand Down
Loading