In my code, I have a factory function that generates a custom on()
event listener tailored to specific event types allowed for user listening. These events are defined with types, each containing an eventName
and data
(which is the emitted event data). My goal is to link these two elements so that when listening for a particular event, the corresponding data will be accessible in the handler.
Here is an example of the implementation:
interface DefaultEventBody {
eventName: string
data: unknown
}
interface ChristmasEvent extends DefaultEventBody {
eventName: 'christmas',
data: {
numberOfPresentsGiven: number
}
}
interface EasterEvent extends DefaultEventBody {
eventName: 'easter',
data: {
numberOfEggsGiven: number
}
}
export function Listener <EventBody extends DefaultEventBody> () {
return (eventName: EventBody['eventName'], fn: (data: EventBody['data']) => void) => {
// someEmitter.on(eventName, fn)
}
}
const spoiltBrat = { on: Listener<EasterEvent|ChristmasEvent>() }
spoiltBrat.on('christmas', (data) => {
console.log(data.numberOfPresentsGiven)
})
TypeScript correctly recognizes that the passed eventName
can be either christmas
or easter
, but it struggles to infer the type of data
in the handler function, resulting in an error when attempting to access data.numberOfPresentsGiven
.
Error: Property 'numberOfPresentsGiven' does not exist on type '{ numberOfPresentsGiven: number; } | { numberOfEggsGiven: number; }'. Property 'numberOfPresentsGiven' does not exist on type '{ numberOfEggsGiven: number; }'.(2339)
I understand why this issue occurs, as neither ChristmasEvent
nor EasterEvent
contain identical numberOf*
properties. However, I am curious if there is a solution to achieve the desired functionality?
Update
Upon request from Captain Yossarian, here is a Playground Link showcasing the nearly finished script.