I have code like so:
socket.emit(ClientActions.LOAD_GAME_STATE, (response: GameState | ErrorResponse) => {
// use response
});
The trouble is that if I try to access properties I would expect on a GameState object, it predictably gives me an error because those properties don't exist on the ErrorResponse type.
How can I express that the response could be data or an error, and type it and use it accordingly? Is there a recommended way?
CodePudding user response:
That is the recommended way to type it, and to narrow down the type to either success or error, you should use an if statement as a type guard:
socket.emit(ClientActions.LOAD_GAME_STATE, (response: GameState | ErrorResponse) => {
if ('<a property only in ErrorResponse>' in response) {
// response is of type `ErrorResponse`
} else {
// response is of type `GameState`
}
});
CodePudding user response:
For more cohesive and expansible typings I would suggest something like the following (specifics depend on your implementation)
type ResponseTypes = "success" | "error"; // You may want to get more specific/granular here
interface SocketResponse {
type: ResponseTypes;
// Other info on every socket response
}
interface SocketError extends SocketResponse {
type: "error";
errorMessage: string;
}
interface SocketGameState extends SocketResponse {
type: "success";
gameState: GameState;
}
Your original code would become
socket.emit(ClientActions.LOAD_GAME_STATE, (response: SocketGameState | SocketError) => {
if (response.type === "error") {
... // Narrows to SocketError
} else {
... // Narrows to SocketGameState
}
});
I think this solution would allow you the most flexibility to add many new kinds of response types while handling errors in a consistent, intuitively-typed way.