Skip to content

Commit f6ef267

Browse files
authored
refactor(typings): improve emit types (#4817)
This commit fixes several issues with emit types: - calling `emit()` without calling `timeout()` first is now only available for events without acknowledgement - calling `emit()` after calling `timeout()` is now only available for events with an acknowledgement - calling `emitWithAck()` is now only available for events with an acknowledgement - `timeout()` must be called before calling `emitWithAck()`
1 parent 1cdf36b commit f6ef267

File tree

11 files changed

+631
-321
lines changed

11 files changed

+631
-321
lines changed

lib/broadcast-operator.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import type {
88
EventsMap,
99
TypedEventBroadcaster,
1010
DecorateAcknowledgements,
11-
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
1211
AllButLast,
1312
Last,
14-
SecondArg,
13+
FirstNonErrorArg,
14+
EventNamesWithError,
1515
} from "./typed-events";
1616

1717
export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
@@ -177,7 +177,7 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
177177
public timeout(timeout: number) {
178178
const flags = Object.assign({}, this.flags, { timeout });
179179
return new BroadcastOperator<
180-
DecorateAcknowledgementsWithTimeoutAndMultipleResponses<EmitEvents>,
180+
DecorateAcknowledgements<EmitEvents>,
181181
SocketData
182182
>(this.adapter, this.rooms, this.exceptRooms, flags);
183183
}
@@ -300,10 +300,10 @@ export class BroadcastOperator<EmitEvents extends EventsMap, SocketData>
300300
*
301301
* @return a Promise that will be fulfilled when all clients have acknowledged the event
302302
*/
303-
public emitWithAck<Ev extends EventNames<EmitEvents>>(
303+
public emitWithAck<Ev extends EventNamesWithError<EmitEvents>>(
304304
ev: Ev,
305305
...args: AllButLast<EventParams<EmitEvents, Ev>>
306-
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
306+
): Promise<FirstNonErrorArg<Last<EventParams<EmitEvents, Ev>>>> {
307307
return new Promise((resolve, reject) => {
308308
args.push((err, responses) => {
309309
if (err) {
@@ -516,11 +516,10 @@ export class RemoteSocket<EmitEvents extends EventsMap, SocketData>
516516
*
517517
* @param timeout
518518
*/
519-
public timeout(timeout: number) {
520-
return this.operator.timeout(timeout) as BroadcastOperator<
521-
DecorateAcknowledgements<EmitEvents>,
522-
SocketData
523-
>;
519+
public timeout(
520+
timeout: number
521+
): BroadcastOperator<DecorateAcknowledgements<EmitEvents>, SocketData> {
522+
return this.operator.timeout(timeout);
524523
}
525524

526525
public emit<Ev extends EventNames<EmitEvents>>(

lib/index.ts

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ import {
3636
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
3737
AllButLast,
3838
Last,
39-
FirstArg,
40-
SecondArg,
39+
RemoveAcknowledgements,
40+
EventNamesWithAck,
41+
FirstNonErrorArg,
4142
} from "./typed-events";
4243
import { patchAdapter, restoreAdapter, serveFile } from "./uws";
4344
import corsMiddleware from "cors";
@@ -140,7 +141,7 @@ export class Server<
140141
SocketData = any
141142
> extends StrictEventEmitter<
142143
ServerSideEvents,
143-
EmitEvents,
144+
RemoveAcknowledgements<EmitEvents>,
144145
ServerReservedEventsMap<
145146
ListenEvents,
146147
EmitEvents,
@@ -846,26 +847,6 @@ export class Server<
846847
return this.sockets.except(room);
847848
}
848849

849-
/**
850-
* Emits an event and waits for an acknowledgement from all clients.
851-
*
852-
* @example
853-
* try {
854-
* const responses = await io.timeout(1000).emitWithAck("some-event");
855-
* console.log(responses); // one response per client
856-
* } catch (e) {
857-
* // some clients did not acknowledge the event in the given delay
858-
* }
859-
*
860-
* @return a Promise that will be fulfilled when all clients have acknowledged the event
861-
*/
862-
public emitWithAck<Ev extends EventNames<EmitEvents>>(
863-
ev: Ev,
864-
...args: AllButLast<EventParams<EmitEvents, Ev>>
865-
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
866-
return this.sockets.emitWithAck(ev, ...args);
867-
}
868-
869850
/**
870851
* Sends a `message` event to all clients.
871852
*
@@ -882,7 +863,9 @@ export class Server<
882863
* @return self
883864
*/
884865
public send(...args: EventParams<EmitEvents, "message">): this {
885-
this.sockets.emit("message", ...args);
866+
// This type-cast is needed because EmitEvents likely doesn't have `message` as a key.
867+
// if you specify the EmitEvents, the type of args will be never.
868+
this.sockets.emit("message" as any, ...args);
886869
return this;
887870
}
888871

@@ -892,7 +875,9 @@ export class Server<
892875
* @return self
893876
*/
894877
public write(...args: EventParams<EmitEvents, "message">): this {
895-
this.sockets.emit("message", ...args);
878+
// This type-cast is needed because EmitEvents likely doesn't have `message` as a key.
879+
// if you specify the EmitEvents, the type of args will be never.
880+
this.sockets.emit("message" as any, ...args);
896881
return this;
897882
}
898883

@@ -948,10 +933,10 @@ export class Server<
948933
*
949934
* @return a Promise that will be fulfilled when all servers have acknowledged the event
950935
*/
951-
public serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(
936+
public serverSideEmitWithAck<Ev extends EventNamesWithAck<ServerSideEvents>>(
952937
ev: Ev,
953938
...args: AllButLast<EventParams<ServerSideEvents, Ev>>
954-
): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
939+
): Promise<FirstNonErrorArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
955940
return this.sockets.serverSideEmitWithAck(ev, ...args);
956941
}
957942

lib/namespace.ts

Lines changed: 52 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ import {
99
DecorateAcknowledgementsWithTimeoutAndMultipleResponses,
1010
AllButLast,
1111
Last,
12-
FirstArg,
13-
SecondArg,
12+
DecorateAcknowledgementsWithMultipleResponses,
13+
DecorateAcknowledgements,
14+
RemoveAcknowledgements,
15+
EventNamesWithAck,
16+
FirstNonErrorArg,
17+
EventNamesWithoutAck,
1418
} from "./typed-events";
1519
import type { Client } from "./client";
1620
import debugModule from "debug";
@@ -117,7 +121,7 @@ export class Namespace<
117121
SocketData = any
118122
> extends StrictEventEmitter<
119123
ServerSideEvents,
120-
EmitEvents,
124+
RemoveAcknowledgements<EmitEvents>,
121125
NamespaceReservedEventsMap<
122126
ListenEvents,
123127
EmitEvents,
@@ -252,7 +256,10 @@ export class Namespace<
252256
* @return a new {@link BroadcastOperator} instance for chaining
253257
*/
254258
public to(room: Room | Room[]) {
255-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).to(room);
259+
return new BroadcastOperator<
260+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
261+
SocketData
262+
>(this.adapter).to(room);
256263
}
257264

258265
/**
@@ -268,7 +275,10 @@ export class Namespace<
268275
* @return a new {@link BroadcastOperator} instance for chaining
269276
*/
270277
public in(room: Room | Room[]) {
271-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).in(room);
278+
return new BroadcastOperator<
279+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
280+
SocketData
281+
>(this.adapter).in(room);
272282
}
273283

274284
/**
@@ -290,9 +300,10 @@ export class Namespace<
290300
* @return a new {@link BroadcastOperator} instance for chaining
291301
*/
292302
public except(room: Room | Room[]) {
293-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).except(
294-
room
295-
);
303+
return new BroadcastOperator<
304+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
305+
SocketData
306+
>(this.adapter).except(room);
296307
}
297308

298309
/**
@@ -430,7 +441,7 @@ export class Namespace<
430441
*
431442
* @return Always true
432443
*/
433-
public emit<Ev extends EventNames<EmitEvents>>(
444+
public emit<Ev extends EventNamesWithoutAck<EmitEvents>>(
434445
ev: Ev,
435446
...args: EventParams<EmitEvents, Ev>
436447
): boolean {
@@ -440,30 +451,6 @@ export class Namespace<
440451
);
441452
}
442453

443-
/**
444-
* Emits an event and waits for an acknowledgement from all clients.
445-
*
446-
* @example
447-
* const myNamespace = io.of("/my-namespace");
448-
*
449-
* try {
450-
* const responses = await myNamespace.timeout(1000).emitWithAck("some-event");
451-
* console.log(responses); // one response per client
452-
* } catch (e) {
453-
* // some clients did not acknowledge the event in the given delay
454-
* }
455-
*
456-
* @return a Promise that will be fulfilled when all clients have acknowledged the event
457-
*/
458-
public emitWithAck<Ev extends EventNames<EmitEvents>>(
459-
ev: Ev,
460-
...args: AllButLast<EventParams<EmitEvents, Ev>>
461-
): Promise<SecondArg<Last<EventParams<EmitEvents, Ev>>>> {
462-
return new BroadcastOperator<EmitEvents, SocketData>(
463-
this.adapter
464-
).emitWithAck(ev, ...args);
465-
}
466-
467454
/**
468455
* Sends a `message` event to all clients.
469456
*
@@ -482,7 +469,9 @@ export class Namespace<
482469
* @return self
483470
*/
484471
public send(...args: EventParams<EmitEvents, "message">): this {
485-
this.emit("message", ...args);
472+
// This type-cast is needed because EmitEvents likely doesn't have `message` as a key.
473+
// if you specify the EmitEvents, the type of args will be never.
474+
this.emit("message" as any, ...args);
486475
return this;
487476
}
488477

@@ -492,7 +481,9 @@ export class Namespace<
492481
* @return self
493482
*/
494483
public write(...args: EventParams<EmitEvents, "message">): this {
495-
this.emit("message", ...args);
484+
// This type-cast is needed because EmitEvents likely doesn't have `message` as a key.
485+
// if you specify the EmitEvents, the type of args will be never.
486+
this.emit("message" as any, ...args);
496487
return this;
497488
}
498489

@@ -557,10 +548,10 @@ export class Namespace<
557548
*
558549
* @return a Promise that will be fulfilled when all servers have acknowledged the event
559550
*/
560-
public serverSideEmitWithAck<Ev extends EventNames<ServerSideEvents>>(
551+
public serverSideEmitWithAck<Ev extends EventNamesWithAck<ServerSideEvents>>(
561552
ev: Ev,
562553
...args: AllButLast<EventParams<ServerSideEvents, Ev>>
563-
): Promise<FirstArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
554+
): Promise<FirstNonErrorArg<Last<EventParams<ServerSideEvents, Ev>>>[]> {
564555
return new Promise((resolve, reject) => {
565556
args.push((err, responses) => {
566557
if (err) {
@@ -612,9 +603,10 @@ export class Namespace<
612603
* @return self
613604
*/
614605
public compress(compress: boolean) {
615-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).compress(
616-
compress
617-
);
606+
return new BroadcastOperator<
607+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
608+
SocketData
609+
>(this.adapter).compress(compress);
618610
}
619611

620612
/**
@@ -630,7 +622,10 @@ export class Namespace<
630622
* @return self
631623
*/
632624
public get volatile() {
633-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).volatile;
625+
return new BroadcastOperator<
626+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
627+
SocketData
628+
>(this.adapter).volatile;
634629
}
635630

636631
/**
@@ -645,7 +640,10 @@ export class Namespace<
645640
* @return a new {@link BroadcastOperator} instance for chaining
646641
*/
647642
public get local() {
648-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).local;
643+
return new BroadcastOperator<
644+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
645+
SocketData
646+
>(this.adapter).local;
649647
}
650648

651649
/**
@@ -664,10 +662,18 @@ export class Namespace<
664662
*
665663
* @param timeout
666664
*/
667-
public timeout(timeout: number) {
668-
return new BroadcastOperator<EmitEvents, SocketData>(this.adapter).timeout(
669-
timeout
670-
);
665+
public timeout(
666+
timeout: number
667+
): BroadcastOperator<
668+
DecorateAcknowledgements<
669+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>
670+
>,
671+
SocketData
672+
> {
673+
return new BroadcastOperator<
674+
DecorateAcknowledgementsWithMultipleResponses<EmitEvents>,
675+
SocketData
676+
>(this.adapter).timeout(timeout);
671677
}
672678

673679
/**

lib/parent-namespace.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { Namespace } from "./namespace";
22
import type { Server, RemoteSocket } from "./index";
33
import type {
44
EventParams,
5-
EventNames,
65
EventsMap,
76
DefaultEventsMap,
7+
EventNamesWithoutAck,
88
} from "./typed-events";
99
import type { BroadcastOptions } from "socket.io-adapter";
1010
import debugModule from "debug";
@@ -56,7 +56,7 @@ export class ParentNamespace<
5656
this.adapter = { broadcast };
5757
}
5858

59-
public emit<Ev extends EventNames<EmitEvents>>(
59+
public emit<Ev extends EventNamesWithoutAck<EmitEvents>>(
6060
ev: Ev,
6161
...args: EventParams<EmitEvents, Ev>
6262
): boolean {

lib/socket.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,10 @@ import {
77
DecorateAcknowledgementsWithMultipleResponses,
88
DefaultEventsMap,
99
EventNames,
10+
EventNamesWithAck,
1011
EventParams,
1112
EventsMap,
12-
FirstArg,
13+
FirstNonErrorArg,
1314
Last,
1415
StrictEventEmitter,
1516
} from "./typed-events";
@@ -383,10 +384,10 @@ export class Socket<
383384
*
384385
* @return a Promise that will be fulfilled when the client acknowledges the event
385386
*/
386-
public emitWithAck<Ev extends EventNames<EmitEvents>>(
387+
public emitWithAck<Ev extends EventNamesWithAck<EmitEvents>>(
387388
ev: Ev,
388389
...args: AllButLast<EventParams<EmitEvents, Ev>>
389-
): Promise<FirstArg<Last<EventParams<EmitEvents, Ev>>>> {
390+
): Promise<FirstNonErrorArg<Last<EventParams<EmitEvents, Ev>>>> {
390391
// the timeout flag is optional
391392
const withErr = this.flags.timeout !== undefined;
392393
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)