deno-websocket icon indicating copy to clipboard operation
deno-websocket copied to clipboard

Add type annotations to events of WebSocketServer/Client

Open cdrini opened this issue 2 years ago • 3 comments

Being able to having things like connection auto-complete would be very helpful. Eg.

image

image

cdrini avatar Oct 03 '21 02:10 cdrini

Looking at VS code's internals, they do something like this for HTML:

interface DocumentEventMap extends GlobalEventHandlersEventMap, DocumentAndElementEventHandlersEventMap {
    "fullscreenchange": Event;
    "fullscreenerror": Event;
    "pointerlockchange": Event;
    "pointerlockerror": Event;
    "readystatechange": Event;
    "visibilitychange": Event;
}

/** The HTMLDocument property of Window objects is an alias that browsers expose for the Document interface object. */
interface HTMLDocument extends Document {
    addEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: HTMLDocument, ev: DocumentEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
    addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
    removeEventListener<K extends keyof DocumentEventMap>(type: K, listener: (this: HTMLDocument, ev: DocumentEventMap[K]) => any, options?: boolean | EventListenerOptions): void;
    removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
}

For this class, which extends Deno's EventEmitter, something like this seems to do the trick!

type WebSockerServerEventMap = {
  'connection': (ws: WebSocketClient) => any,
  'error': (error: Error) => any,
}

class WSServer extends EventEmitter  {
  on<K extends keyof WSServerEventMap>(eventName: K, listener: WSServerEventMap[K]): this;
  on(eventName: string | symbol, listener: GenericFunction | WrappedFunction): this {
    return super.on(eventName, listener);
  }

  once<K extends keyof WSServerEventMap>(eventName: K, listener: WSServerEventMap[K]): this;
  once(eventName: string | symbol, listener: GenericFunction): this {
    return super.once(eventName, listener);
  }

  addListener<K extends keyof WSServerEventMap>(eventName: K, listener: WSServerEventMap[K]): this;
  addListener(eventName: string | symbol, listener: GenericFunction | WrappedFunction): this {
    return super.addListener(eventName, listener);
  }
}

Not a huge fan of having to wrap the super method; I feel like there should be a type-only way to do this? But can't seem to find it :/

cdrini avatar Oct 03 '21 02:10 cdrini

// copy from #31 Hmm... I did that in https://github.com/ryo-ma/deno-websocket/pull/34 but I was only modifying "on" and "emit" since I rarely use other API parts. I think I can add more typings :eyes:

// Actually I think about checking out why the EventEmitter from std library doesn't have (not sure atm) an exported EventEmitter set up in the way that allows passing event map, because I feel this is weird the std module of typescript runtime doesn't do that as default.

Danielduel avatar Apr 14 '22 14:04 Danielduel

Waiting for update in std library, hopefully they will approve my changes :) https://github.com/denoland/deno_std/pull/2111

Danielduel avatar Apr 14 '22 19:04 Danielduel