'React Native TypeScript WebSocket onerror Value of "this" must be of type Event

I'm trying to implement WebSocket support into my app by following React Native's own tutorial at https://reactnative.dev/docs/network#websocket-support.

Here is my code:

function initializeSocket(socket:WebSocket){
    socket.onopen = () => {
        store.dispatch(toggleWebsocketConnected(true));
    };
    socket.onclose = () => {
        store.dispatch(toggleWebsocketConnected(false));
    };
    socket.onerror = (e:any) => {
        console.log('websocket error', e);
        store.dispatch(toggleWebsocketConnected(false));
    };
    socket.onmessage = (e) => {
        processMessage(e);
    };
}

In onerror event handler (for now, ignore why error handler is called, this is not a "why can't I connect" question, but a TypeScript question) when it's triggered I'm getting a weird error at runtime:

TypeError [ERR_INVALID_THIS]: Value of "this" must be of type Event
    at new NodeError (/path/to/my/project/lib/internal/errors.js:371:5)
    at Event.[nodejs.util.inspect.custom] (/path/to/my/project/lib/internal/event_target.js:120:13)
    at formatValue (/path/to/my/project/lib/internal/util/inspect.js:763:19)
    at inspect (/path/to/my/project/lib/internal/util/inspect.js:340:10)
    at formatWithOptionsInternal (/path/to/my/project/lib/internal/util/inspect.js:2006:40)
    at formatWithOptions (/path/to/my/project/lib/internal/util/inspect.js:1888:10)
    at console.value (/path/to/my/project/lib/internal/console/constructor.js:323:14)
    at console.log (/path/to/my/project/lib/internal/console/constructor.js:359:61)
    at EventTarget.socket.onerror (/path/to/my/project/src/services/myproject/socket.ts:27:17)
    at EventTarget.dispatchEvent (/path/to/my/project/.vscode/.react/index.bundle:31193:27) {code: 'ERR_INVALID_THIS', stack: 'TypeError [ERR_INVALID_THIS]: Value of "this"…/myproject/.vscode/.react/index.bundle:31193:27)', message: 'Value of "this" must be of type Event', toString: ƒ, Symbol(kIsNodeError): true}

Vscode linter shows everything is fine though. When I hover over the onerror function it shows:

(property) WebSocket.onerror: ((this: WebSocket, ev: Event) => any) | null

Two input arguments. So eventhough Vscode isn't complaining about my initial code, I also tried to change it to the following:

 socket.onerror = (_:any, e:any) => {
    console.log('websocket error', e);
    store.dispatch(toggleWebsocketConnected(false));
 };

Now, Vscode linter shows an error:

Type '(_: any, e: any) => void' is not assignable to type '(this: WebSocket, ev: Event) => any'.ts(2322)

If I run the code regardless of the error, I don't get a runtime error, yet _ is now an actual event object and e is undefined:

enter image description here

What am I doing wrong? What is the correct form of doing it? I've stumbled upon a weird problem with this keyword in arguments and TypeScript before (see Why are rest parameters undefined in TypeScript?) which indeed turned to be a Babel bug, which is now merged and fixed long ago.

How can I get it to work correectly?

UPDATE: I've managed to "work around" by turning the arrow function into a function with a function keyword:

    socket.onerror = function(this:any, e:Event){
        console.log('websocket error', e);
        store.dispatch(toggleWebsocketConnected(false));
    };

It "works" now, though this is not an actual solution as I still can't use arrow functions, breaking my code style as I never use function keyword functions in code (other than top-level simple definitions) anywhere in the app.

No it doesn't, it seemed to step correctly yet actually threw an error again when I reached a line to console.log the event.



Solution 1:[1]

Example of errors

var wsWrong = new WebSocket('ws://host.com/path')

wsWrong.onerror = (
    ev // (parameter) e: Event
) => {

Firstly, wsWrong.onerror has type:

(property) WebSocket.onerror: ((this: WebSocket, ev: Event) => any) | null

And we can see the typeof this in the arrow function would be

this: typeof globalThis

The containing arrow function captures the global value of 'this'.(7041)

  this

Also, An arrow function cannot have a 'this' parameter.(2730) so that we can' t assign this type to arrow function. we need to use a function to fit TypeScript's need.

Secondly, Because event type does not have a message property, the following will trigger an error: Property 'message' does not exist on type 'Event'.(2339) MDN: Event

  console.log(ev.message)
}

To fix it, example of correct way

var wsRight = new WebSocket('ws://host.com/path')

The follow line use a function here and TypeScript will resolve this for you. i.e. 1 argument needed.

wsRight.onerror = function(ev) {

We use instanceof Narrowing here MDN: ErrorEvent

  if(ev instanceof ErrorEvent) {
    console.log(ev.message) // and now TS can found the `message` property.
  }
}

TypeScript Playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Edwin