Given this union of events:
type Events =
| { type: 'CREATE'; data: { foo: string } }
| { type: 'READ'; uuid: string }
| { type: 'UPDATE'; data: { foo: string; uuid: string } };
I've made functions that processes the events, and also I collected a map:
const processors: Processors<Events> = {
CREATE: (event) => {
console.log(event.data.foo);
},
READ: (event) => {
console.log(event.uuid);
},
UPDATE: (event) => {
console.log(event.data.uuid);
},
}
The Processors<Event>
type isn't complex and works predictably:
type Processors<E extends EventObject> =
{ [T in E['type']]: (event: Extract<E, { type: T}>) => void };
I've got a problem when trying to use these functions to handle events:
function(event: Events) {
const receiver = processors[event.type]; // this produces (event: never) => void
receiver(event);
^^^^^ error
}
The argument type of the receiver is intersection
because it comes from contra-variant position in Processors<Event>
type:
receiver(event:
{ type: 'CREATE'; data: { foo: string } } &
{ type: 'READ'; uuid: string } &
{ type: 'UPDATE'; data: { foo: string; uuid: string } }
);
I assume the Events union is not assignable to the Events intersection.
If I discriminate the union, the problem goes away:
if (event.type === 'CREATE') {
const receiver = processors[event.type]
receiver(event);
}
But this is getting unreasonable as we know that event always has one and only one type at any given time.
Is it possible to solve the problem without type casting? It is as if you can convert the intersection into the union, discriminate intersection.
function(event: Events) {
const receiver = processors[event.type]; // this produces (event: never) => void
receiver(event);
^^^^^ How to solve the problem without useless and redundant code?
}