Let's say I have this code -- I know it's wrong.
enum SocketClient {
PLAYER_LOGIN = "player:login",
PLAYER_LOGOUT = "player:logout",
}
enum SocketClientPayload {
PLAYER_LOGIN = { user: string; pass: string};
PLAYER_LOGOUT = { uuid: string;}
}
type wsMessage = {
event: typeof SocketClient;
payload: SocketClientPayload[SocketClient];
};
I want the payload at the bottom to be tied to the whatever I specify for the incoming SocketEvent so that every SocketEvent's payload is typed. Is there a way to do dynamic form types?
CodePudding user response:
You can use a Mapped Type for this. For example:
enum SocketClient {
PLAYER_LOGIN = "player:login",
PLAYER_LOGOUT = "player:logout",
}
type SocketClientPayload = {
[SocketClient.PLAYER_LOGIN]: { user: string; pass: string; };
[SocketClient.PLAYER_LOGOUT]: { uuid: string; };
};
type wsMessage<T extends SocketClient> = {
event: T;
payload: SocketClientPayload[T];
};
let test: wsMessage<SocketClient.PLAYER_LOGOUT> = {
event: SocketClient.PLAYER_LOGOUT,
payload: { uuid: 'test' }
};
By making your wsMessage type generic, you can have it figure out which payload type to use based on which member of your SocketClient enum was used to instantiate it.
Note that TypeScript doesn't infer generic types when used like this, which is why I've had to specify that the test variable I've created is of type SocketClient.PLAYER_LOGOUT even though that could theoretically be determined based on the value of the event property. But TypeScript can infer this when used in a generic function. For example:
function processPayload<T extends SocketClient>(message: wsMessage<T>) {
// Do something
}
// TypeScript can see the argument is of type `wsMessage<SocketClient.PLAYER_LOGIN>` based on its `event` property, and it infers the generic type of the function from that.
processPayload(
{
event: SocketClient.PLAYER_LOGIN,
payload: { user: 'test', pass: 'test' },
},
);
