ryo-ma / deno-websocket

🦕 A simple WebSocket library like ws of node.js library for deno
https://deno.land/x/websocket
MIT License
152 stars 17 forks source link

Add type annotations to events of WebSocketServer/Client #30

Open cdrini opened 2 years ago

cdrini commented 2 years ago

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

image

image

cdrini commented 2 years ago

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 :/

Danielduel commented 2 years ago

// 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 commented 2 years ago

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