enum ErrorCode {
OK = "OK",
MAXIMUM_EXCEEDED = "MAXIMUM_EXCEEDED",
UNKNOWN_ERROR = "UNKNOWN_ERROR",
}
type Response = {
code: ErrorCode;
message: string;
data?: any;
};
type Person = {
name: string;
age: number;
};
type MaximumExceeded = {
limit: number;
inclusive: boolean;
};
export async function api(id: string, maximum: number) {
try {
if (maximum > 10) {
return {
code: ErrorCode.MAXIMUM_EXCEEDED,
message: "maximum exceeded",
data: {
limit: 10,
inclusive: false,
},
};
}
const result = await new Promise((resolve, reject) => {
setTimeout(() => {
resolve([
{
name: "Alice",
age: 30,
},
{
name: "Bob",
age: 31,
},
]);
}, 1000);
});
return {
code: ErrorCode.OK,
message: "success",
data: result,
};
} catch (e) {
if (e instanceof Error) {
return {
code: ErrorCode.UNKNOWN_ERROR,
message: e.message,
};
} else {
return {
code: ErrorCode.UNKNOWN_ERROR,
message: "Unknown error",
};
}
}
}
async function test() {
const result = await api("123", 20);
if (result.code === ErrorCode.OK) {
console.log(result.data?.name);
} else if (result.code == ErrorCode.MAXIMUM_EXCEEDED) {
console.log("limit", result.data?.limit);
} else {
console.log(result.message);
}
}
I have some predefined error codes.
The result of the api
function follows the same pattern, it alway has a code for program, a message for human and a optional data field. The promise inside is some external resources out of controll.
What I want to do is that I want to caller to get the right type by asserting on the error code. Thanks a lot.
You should look into Discriminated Unions, sometimes also called Tagged Unions
enum ErrorCode {
OK = "OK",
MAXIMUM_EXCEEDED = "MAXIMUM_EXCEEDED",
UNKNOWN_ERROR = "UNKNOWN_ERROR",
}
interface Response<Code, Data> {
code: Code;
message: string;
data: Data;
}
type Person = {
name: string;
age: number;
};
type MaximumExceeded = {
limit: number;
inclusive: boolean;
};
type GetUserResponse =
| Response<ErrorCode.OK, Person>
| Response<ErrorCode.MAXIMUM_EXCEEDED, MaximumExceeded>
| Response<ErrorCode.UNKNOWN_ERROR, unknown>;
function example(response: GetUserResponse) {
if (response.status === ErrorCode.OK) {
// response.data is Person
console.log(response.data.name);
} else if (response.status === ErrorCode.MAXIMUM_EXCEEDED) {
// response.data is MaximumExceeded
console.log(response.data.limit);
}
}
What this does is that, when you check for a status, typescript is able to narrow down the type of response.data
within that if()
block