Im trying to implement chat through OpenTok API using 'signal' events to publish/subscribe.
This is my event listener which works (the signal is received):
// Listen for signal CHAT_MESSAGE
sess.on('signal:CHAT_MESSAGE', event => {
const message = event.data
...
})
Problem is that typescript doesn't recognize event.data
as a valid property. The type comes from the Session class:
Session.signal: Event<'signal', Session> & {
type?: string;
data?: string;
from: Connection;
};
I've tried picking the type from the Session class like
const message = event.data as Session['signal']
Typescript complains Property 'data' does not exist on type 'Event<string, any>'.
I suspect it's because TS doesn't recognize the event type correctly...
I then tried converting to 'unknown' first:
const signal = (event as unknown) as Session['signal']
const msg = signal.data
Now TS complains: Property 'data' does not exist on type '(signal: { type?: string | undefined; data?: string | undefined; to?: Connection | undefined; }, callback: (error?: OTError | undefined) => void) => void'.ts(2339)
I'm not sure why it doesn't think that data
is a prop when at the same time seemingly it says that it is...
How can I fix this problem, preferably without disabling TS type checking?
The problem is Session['signal']
is a function:
signal(
signal: { type?: string, data?: string, to?: Connection },
callback: (error?: OTError) => void
): void;
And the type you probably need is coming from the Session
's class ancestor OTEventEmitter
:
export class Session extends OTEventEmitter<{
...
signal: Event<'signal', Session> & {
type?: string;
data?: string;
from: Connection;
};
...
OTEventEmitter.on
method typings:
class OTEventEmitter<EventMap> {
on<EventName extends keyof EventMap>(
eventName: EventName,
callback: (event: EventMap[EventName]) => void,
context?: object
): void;
on(
eventName: string,
callback: (event: Event<string, any>) => void,
context?: object
): void;
...
As you may notice when Session
extends OTEventEmitter
there is no signal:CHAT_MESSAGE
in the EventMap
. Only signal
.
Thus if you want your signal:CHAT_MESSAGE
to be treated as a signal
event you should write it as:
sess.on('signal:CHAT_MESSAGE' as 'signal', event => {
const message = event.data // no error
// `event` has type
// Event<'signal', Session> & {
// type?: string;
// data?: string;
// from: Connection;
// }
...
This typecasting has no runtime artefacts. It's only purpose to ensure typescript that your signal:CHAT_MESSAGE
event has the same structure as the signal
's one. Though as always with as
keyword now you're responsible for that to be true.