如何创建带错误码的自定义类型响应,使调用者通过断言获取正确类型?
如何通过错误码断言获取API返回的对应类型
要实现调用者通过错误码断言获取对应正确类型,核心是用联合类型替代单一的Response类型,让TypeScript能根据code的字面量值自动缩小返回值的类型范围。以下是具体改造步骤:
1. 重构Response为联合类型
把原来通用的Response拆分为与每个错误码一一对应的类型,明确每个类型的data结构:
enum ErrorCode { OK = "OK", MAXIMUM_EXCEEDED = "MAXIMUM_EXCEEDED", UNKNOWN_ERROR = "UNKNOWN_ERROR", } type Person = { name: string; age: number; }; type MaximumExceeded = { limit: number; inclusive: boolean; }; // 每个错误码对应专属的返回类型 type OkResponse = { code: ErrorCode.OK; message: string; data: Person[]; // OK时data是Person数组 }; type MaximumExceededResponse = { code: ErrorCode.MAXIMUM_EXCEEDED; message: string; data: MaximumExceeded; // 超出限制时data是固定结构 }; type UnknownErrorResponse = { code: ErrorCode.UNKNOWN_ERROR; message: string; data?: never; // 未知错误无data,用never明确不需要 }; // 最终的Response是三个类型的联合 type Response = OkResponse | MaximumExceededResponse | UnknownErrorResponse;
2. 修改API函数的返回类型
让API函数的返回值类型为Promise<Response>,TypeScript会自动验证返回值是否符合联合类型的某个成员:
export async function api(id: string, maximum: number): Promise<Response> { try { if (maximum > 10) { return { code: ErrorCode.MAXIMUM_EXCEEDED, message: "maximum exceeded", data: { limit: 10, inclusive: false, }, }; } const result = await new Promise<Person[]>((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", }; } } }
3. 调用时自动推导类型
现在调用API时,只要判断code的值,TypeScript会自动缩小返回值的类型,无需手动断言:
async function test() { const result = await api("123", 20); if (result.code === ErrorCode.OK) { // TypeScript自动推断result.data是Person[] console.log(result.data[0].name); // 直接访问,无类型错误 } else if (result.code === ErrorCode.MAXIMUM_EXCEEDED) { // 自动推断result.data是MaximumExceeded console.log("limit", result.data.limit); // 直接访问limit } else { // 自动推断是UnknownErrorResponse,无data console.log(result.message); } }
为什么要这么做?
原来的单一Response类型中,code是宽泛的ErrorCode枚举,data是any,TypeScript无法关联code和data的对应关系。而联合类型的每个成员都用字面量类型指定code,TypeScript可以通过code的判断进行类型窄化,自动识别对应的data结构,既保证类型安全,又无需手动写类型断言。
如果确实需要手动断言(比如特殊场景),也可以用类型断言语法:
// 手动断言为MaximumExceededResponse const exceededResult = result as MaximumExceededResponse; console.log(exceededResult.data.limit);
但优先推荐用类型窄化的方式,避免手动断言带来的类型风险。
内容的提问来源于stack exchange,提问作者Evilolipop
相关产品推荐
相关产品推荐

