'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:
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.
}
}
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 |