EventSource named events using TypeScript
Asked Answered
S

2

9

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?

Seeseebeck answered 3/3, 2021 at 22:37 Comment(1)
Looks like there is a generic on EventSource.addEventListener addEventListener<K extends keyof EventSourceEventMap>(type: K, .... but it but it only accepts "error" | "message" | "open" so that's not helpful.Tightlipped
S
6

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);

Strontia answered 30/7, 2021 at 18:2 Comment(0)
F
2

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.

Fungous answered 28/11, 2021 at 2:17 Comment(1)
This worked for me, except that MyDataInterface in the above code should always be string. The MessageEvent interface is for more general use than just SSE. In the specific context of SSE, the data property is always a string, which may contain a serialized object.Shulamite

© 2022 - 2024 — McMap. All rights reserved.