typescript// nothing stops you from having both data and error at once
type ApiResponse = {
data?: User
error?: string
loading?: boolean
}
// consumer has no idea what to check
function handleResponse(res: ApiResponse) {
if (res.data) { /* maybe fine */ }
// what if data AND error are both set?
}
type ApiResponse =
| { status: 'loading' }
| { status: 'success'; data: User }
| { status: 'error'; message: string }
function handleResponse(res: ApiResponse) {
switch (res.status) {
case 'loading':
return showSpinner()
case 'success':
// TypeScript KNOWS res.data exists here
return renderUser(res.data)
case 'error':
// TypeScript KNOWS res.message exists here
return showError(res.message)
}
}适用场景:接口状态、表单提交流程、异步请求结果这类「同一时刻只能处于一种状态」的数据建模,避免 data 和 error 同时存在。
用法示例:
tstype ApiResponse =
| { status: "loading" }
| { status: "success"; data: User }
| { status: "error"; message: string };
function handleResponse(res: ApiResponse) {
// 用统一的 status 分支收窄类型,避免手动判断字段是否存在
if (res.status === "success") return renderUser(res.data);
if (res.status === "error") return showError(res.message);
return showSpinner();
}参数与返回:核心是联合类型中的公共字段 status: string literal,每个分支携带自己的字段。函数接收 ApiResponse 后,TypeScript 会按 status 自动收窄类型。
常见坑:不要用一堆可选字段表示互斥状态,否则会出现 data、error 同时存在的非法组合;新增状态后建议用 switch 加 never 做穷尽检查。

扫码关注「前端达人」,干货代码每周更新