TypeScript 联合类型:让任务加载状态与数据保持一致
把等待、成功和失败表示成可辨识联合,按状态访问对应字段,并用 never 检查遗漏分支,减少互相矛盾的布尔状态。
本节目标
把等待、成功和失败表示成可辨识联合,按状态访问对应字段,并用 never 检查遗漏分支,减少互相矛盾的布尔状态。
环境与运行方法
安装 Node.js 与项目内 TypeScript 编译器。保存为 lesson.ts,执行 npx tsc lesson.ts --strict --target ES2020 --module commonjs --outDir dist,再运行 node dist/lesson.js。编译成功不等于外部数据已经校验。
核心思路
分别维护 loading、error、data 容易构造同时成功又失败的组合。将每种状态与它携带的数据放在同一个联合成员里,可以让不合法组合更难出现。
完整示例
type LoadState =
| {kind: "loading"}
| {kind: "success"; title: string}
| {kind: "error"; message: string};
function label(state: LoadState): string {
switch (state.kind) {
case "loading": return "loading";
case "success": return state.title;
case "error": return "error=" + state.message;
default: {
const unreachable: never = state;
return unreachable;
}
}
}
for (const state of [
{kind: "loading"},
{kind: "success", title: "review"},
{kind: "error", message: "offline"}
] as LoadState[]) console.log(label(state));预期输出
loading
review
error=offline逐步理解
switch 根据 kind 区分分支,成功分支才有 title,失败分支才有 message。default 中将 state 赋给 never,用于在新增成员却忘记处理时产生编译错误。
本例数组是内部声明的数据。真实网络响应仍不能只靠 as LoadState[] 来验证,应按上一节思路校验字段再构造状态。状态类型可以保护程序内的数据流,但不会代替服务端协议检查。
常见问题与边界
- 不要为了避开遗漏分支错误把 never 改成 any。
- 字段全部可选会削弱状态与数据之间的对应关系。
- 类型之外仍要设计用户界面在失败后如何重试。
动手练习
增加 {kind:"empty"} 状态但暂不添加 switch 分支。
参考答案
严格编译应在 never 赋值处提示缺失处理。增加 case "empty" 返回提示后恢复通过,这就是编译期穷尽检查的用途。
文章回复
0 条公开回复