Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EventSource named events using TypeScript

I'm trying to use an EventSource with Typescript but cannot type correctly the response when using named events.

I tried

const evtSource = new EventSource('/my-url');

const parseMyEvent = (evt: Event) => {
  const data: MyDataInterface = JSON.parse(evt.data);
  console.log(data)
}

evtSource.addEventListener('my-event', parseMyEvent);

Fails, because Event has no property data

const evtSource = new EventSource('/my-url');

const parseMyEvent = (evt: MessageEvent) => {
  const data: MyDataInterface = JSON.parse(evt.data);
  console.log(data)
}

evtSource.addEventListener('my-event', parseMyEvent);

Fails on evtSource.addEventListener('my-event', parseMyEvent), with "No overload matches this call. ".

I know that MessageEvent is a generic interface, but what should I use as its type?

I'm using TS 3.5.3 so I tried to install the external type @types/eventsource with no luck too (I know, it is for the polyfill EventSource lib, but I tried)

When using the generic evtSource.onMessage = fn it works without any problems

It should be possible to type the listener/response of a EventSource event in TS, but how?

like image 809
Renato Rodrigues Avatar asked Mar 03 '21 22:03

Renato Rodrigues


2 Answers

The below code should work. Basically, you are using TypeScript's Type assertions and let the compiler trust you that the underlying event is actually an object of type MessageEvent:

const evtSource = new EventSource('/my-url');

const parseMyEvent = (evt: Event) => {
  const messageEvent = (evt as MessageEvent);  // <== This line is Important!!
  const data: MyDataInterface = JSON.parse(messageEvent.data);
  console.log(data)
}

evtSource.addEventListener('my-event', parseMyEvent);

like image 196
farhank Avatar answered Oct 21 '22 15:10

farhank


The addEventListener method on EventSource has a generic that refers to a type mapping interface (EventSourceEventMap). As mentioned in the comments. the generic initially only accepts "error" | "message" | "open", but that's because those are the only types defined in the map by default. The intent is that you augment the map with your own types for whatever custom events you are adding.

So in your case, what you need is:

declare global {
  interface EventSourceEventMap {
    ['my-event']: MessageEvent<MyDataInterface>;
  }
}

With that, typescript will not only know that the message passed to my-event does have a data property, it will know the type of that property as well.

like image 1
Jason Kohles Avatar answered Oct 21 '22 14:10

Jason Kohles