I am currently trying to map data payload types (with no fields strictly in common except their type
field) to handler functions to be called when a payload of that format is received. The dictionary I am using to do this is written similar to this:
const requestHandlers: { [E in Request as E["type"]]: ((server: Server, c: Client, r: E) => void) | undefined } = {
'requestType:format1': requestTypeFormat1Handler,
'requestType2:format1': requestType2Format1Handler,
etc...
}
Where Request
is defined as type Request = | RequestTypeFormat1 | RequestType2Format1 | ...
And an example type under the Request
umbrella is as follows:
export type RequestTypeFormat1 = {
type: 'requestType:format1',
username: string,
room: Room
};
This seems to make sense to me in theory, and there are no errors with the declaration of requestHandlers
as it stands, but when I try to index the dictionary with a variable of the generic Request
type, I get the following error:
Argument of type 'Request' is not assignable to parameter of type 'never'.
The intersection 'RequestTypeFormat1 & RequestType2Format1 & RequestType2Format2' was reduced to 'never' because property 'type' has conflicting types in some constituents.
Type 'RequestTypeFormat1' is not assignable to type 'never'.
Clearly the r: E
parameter in the mapped type definition is being converted into an intersection because it varies between request types. I would like the function to behave more as a union while still providing a strict relation between request types and handler functions with a request parameter appropriate for the corresponding type. Basically, I want the dictionary to function as written, even with generic Request
indexing.
Maybe a different approach entirely would be better, but I can’t think of one.
I did find this answer which solves a problem similar to mine, but the solution is to employ the generic any
type for handler parameters which is less than ideal for my use case.
As a side note, the type of requestHandlers
is resolved by TypeScript to be the following:
const requestHandlers: {
"requestType:format1": ((wss: WSS, c: SocketClient, r: RequestTypeFormat1) => void) | undefined;
"requestType2:format1": ((wss: WSS, c: SocketClient, r: RequestType2Format1) => void) | undefined;
etc...
}