[ws]: Add generics to the ServerOptions (#65501)

* fix(ws): add generics to the server options

* test(ws): add generics to `WebSocket` and `IncomingMesage`
This commit is contained in:
Sergey Bakulin
2023-06-08 22:48:40 +03:00
committed by GitHub
parent 815a3feadd
commit b0683348c2
2 changed files with 107 additions and 34 deletions

63
types/ws/index.d.ts vendored
View File

@@ -231,15 +231,19 @@ declare namespace WebSocket {
* incoming message. The return value (boolean) of the function determines
* whether or not to accept the handshake.
*/
type VerifyClientCallbackSync = (info: { origin: string; secure: boolean; req: IncomingMessage }) => boolean;
type VerifyClientCallbackSync<Request extends IncomingMessage = IncomingMessage> = (info: {
origin: string;
secure: boolean;
req: Request;
}) => boolean;
/**
* VerifyClientCallbackAsync is an asynchronous callback used to inspect the
* incoming message. The return value (boolean) of the function determines
* whether or not to accept the handshake.
*/
type VerifyClientCallbackAsync = (
info: { origin: string; secure: boolean; req: IncomingMessage },
type VerifyClientCallbackAsync<Request extends IncomingMessage = IncomingMessage> = (
info: { origin: string; secure: boolean; req: Request },
callback: (res: boolean, code?: number, message?: string, headers?: OutgoingHttpHeaders) => void,
) => void;
@@ -314,20 +318,26 @@ declare namespace WebSocket {
once?: boolean | undefined;
}
interface ServerOptions {
interface ServerOptions<
U extends typeof WebSocket.WebSocket = typeof WebSocket.WebSocket,
V extends typeof IncomingMessage = typeof IncomingMessage,
> {
host?: string | undefined;
port?: number | undefined;
backlog?: number | undefined;
server?: HTTPServer | HTTPSServer | undefined;
verifyClient?: VerifyClientCallbackAsync | VerifyClientCallbackSync | undefined;
handleProtocols?: (protocols: Set<string>, request: IncomingMessage) => string | false;
server?: HTTPServer<V> | HTTPSServer<V> | undefined;
verifyClient?:
| VerifyClientCallbackAsync<InstanceType<V>>
| VerifyClientCallbackSync<InstanceType<V>>
| undefined;
handleProtocols?: (protocols: Set<string>, request: InstanceType<V>) => string | false;
path?: string | undefined;
noServer?: boolean | undefined;
clientTracking?: boolean | undefined;
perMessageDeflate?: boolean | PerMessageDeflateOptions | undefined;
maxPayload?: number | undefined;
skipUTF8Validation?: boolean | undefined;
WebSocket?: typeof WebSocket.WebSocket | undefined;
WebSocket?: U | undefined;
}
interface AddressInfo {
@@ -337,51 +347,54 @@ declare namespace WebSocket {
}
// WebSocket Server
class Server<T extends WebSocket = WebSocket> extends EventEmitter {
options: ServerOptions;
class Server<
T extends typeof WebSocket.WebSocket = typeof WebSocket.WebSocket,
U extends typeof IncomingMessage = typeof IncomingMessage,
> extends EventEmitter {
options: ServerOptions<T, U>;
path: string;
clients: Set<T>;
clients: Set<InstanceType<T>>;
constructor(options?: ServerOptions, callback?: () => void);
constructor(options?: ServerOptions<T, U>, callback?: () => void);
address(): AddressInfo | string;
close(cb?: (err?: Error) => void): void;
handleUpgrade(
request: IncomingMessage,
request: InstanceType<U>,
socket: Duplex,
upgradeHead: Buffer,
callback: (client: T, request: IncomingMessage) => void,
callback: (client: InstanceType<T>, request: InstanceType<U>) => void,
): void;
shouldHandle(request: IncomingMessage): boolean | Promise<boolean>;
shouldHandle(request: InstanceType<U>): boolean | Promise<boolean>;
// Events
on(event: "connection", cb: (this: Server<T>, socket: T, request: IncomingMessage) => void): this;
on(event: "connection", cb: (this: Server<T>, socket: InstanceType<T>, request: InstanceType<U>) => void): this;
on(event: "error", cb: (this: Server<T>, error: Error) => void): this;
on(event: "headers", cb: (this: Server<T>, headers: string[], request: IncomingMessage) => void): this;
on(event: "headers", cb: (this: Server<T>, headers: string[], request: InstanceType<U>) => void): this;
on(event: "close" | "listening", cb: (this: Server<T>) => void): this;
on(event: string | symbol, listener: (this: Server<T>, ...args: any[]) => void): this;
once(event: "connection", cb: (this: Server<T>, socket: T, request: IncomingMessage) => void): this;
once(event: "connection", cb: (this: Server<T>, socket: InstanceType<T>, request: InstanceType<U>) => void): this;
once(event: "error", cb: (this: Server<T>, error: Error) => void): this;
once(event: "headers", cb: (this: Server<T>, headers: string[], request: IncomingMessage) => void): this;
once(event: "headers", cb: (this: Server<T>, headers: string[], request: InstanceType<U>) => void): this;
once(event: "close" | "listening", cb: (this: Server<T>) => void): this;
once(event: string | symbol, listener: (this: Server<T>, ...args: any[]) => void): this;
off(event: "connection", cb: (this: Server<T>, socket: T, request: IncomingMessage) => void): this;
off(event: "connection", cb: (this: Server<T>, socket: InstanceType<T>, request: InstanceType<U>) => void): this;
off(event: "error", cb: (this: Server<T>, error: Error) => void): this;
off(event: "headers", cb: (this: Server<T>, headers: string[], request: IncomingMessage) => void): this;
off(event: "headers", cb: (this: Server<T>, headers: string[], request: InstanceType<U>) => void): this;
off(event: "close" | "listening", cb: (this: Server<T>) => void): this;
off(event: string | symbol, listener: (this: Server<T>, ...args: any[]) => void): this;
addListener(event: "connection", cb: (client: T, request: IncomingMessage) => void): this;
addListener(event: "connection", cb: (client: InstanceType<T>, request: InstanceType<U>) => void): this;
addListener(event: "error", cb: (err: Error) => void): this;
addListener(event: "headers", cb: (headers: string[], request: IncomingMessage) => void): this;
addListener(event: "headers", cb: (headers: string[], request: InstanceType<U>) => void): this;
addListener(event: "close" | "listening", cb: () => void): this;
addListener(event: string | symbol, listener: (...args: any[]) => void): this;
removeListener(event: "connection", cb: (client: T) => void): this;
removeListener(event: "connection", cb: (client: InstanceType<T>, request: InstanceType<U>) => void): this;
removeListener(event: "error", cb: (err: Error) => void): this;
removeListener(event: "headers", cb: (headers: string[], request: IncomingMessage) => void): this;
removeListener(event: "headers", cb: (headers: string[], request: InstanceType<U>) => void): this;
removeListener(event: "close" | "listening", cb: () => void): this;
removeListener(event: string | symbol, listener: (...args: any[]) => void): this;
}

View File

@@ -329,17 +329,16 @@ function f() {
}
declare module 'ws' {
interface WebSocket {
id?: string;
}
interface Server {
getWebSocketId(): string;
}
}
{
const server = new wslib.WebSocketServer();
class MyWebSocket extends WebSocket {
id?: string;
}
const server = new wslib.WebSocketServer({ WebSocket: MyWebSocket });
server.on('connection', (ws) => {
// $ExpectType string | undefined
@@ -363,10 +362,7 @@ declare module 'ws' {
}
}
const server = new http.Server();
const webSocketServer = new WebSocket.WebSocketServer<CustomWebSocket>({
WebSocket: CustomWebSocket,
noServer: true
});
const webSocketServer = new WebSocket.WebSocketServer({ WebSocket: CustomWebSocket, noServer: true });
webSocketServer.on('connection', (ws) => {
// $ExpectType CustomWebSocket
ws;
@@ -408,3 +404,67 @@ declare module 'ws' {
ws.onclose = null;
ws.onmessage = null;
}
{
class Request extends http.IncomingMessage {}
class MyWebsocket extends WebSocket {}
const server = http.createServer({ IncomingMessage: Request });
const wss = new WebSocket.WebSocketServer({ WebSocket: MyWebsocket, server });
wss.on('connection', (ws, req) => {
// $ExpectType MyWebsocket
ws;
// $ExpectType Request
req;
});
wss.once('connection', (ws, req) => {
// $ExpectType MyWebsocket
ws;
// $ExpectType Request
req;
});
wss.off('connection', (ws, req) => {
// $ExpectType MyWebsocket
ws;
// $ExpectType Request
req;
});
wss.addListener('connection', (ws, req) => {
// $ExpectType MyWebsocket
ws;
// $ExpectType Request
req;
});
wss.removeListener('connection', (ws, req) => {
// $ExpectType MyWebsocket
ws;
// $ExpectType Request
req;
});
wss.on('headers', (_headers, req) => {
// $ExpectType Request
req;
});
wss.once('headers', (_headers, req) => {
// $ExpectType Request
req;
});
wss.off('headers', (_headers, req) => {
// $ExpectType Request
req;
});
wss.addListener('headers', (_headers, req) => {
// $ExpectType Request
req;
});
wss.removeListener('headers', (_headers, req) => {
// $ExpectType Request
req;
});
Array.from(wss.clients).forEach(client => {
// $ExpectType MyWebsocket
client;
});
}