TYPESCRIPT / FOUNDATIONS

类型收窄

从多种可能中确认当前类型,理解可辨识属性、in 与控制流路径。

本章目录CONTENTS ↓

一个值可能属于多种类型时,直接访问某个成员独有的属性会遇到报错。收窄让编译器利用已经执行的判断,确定当前位置还可能有哪些类型,从而允许安全的操作。

什么时候需要收窄

当参数是联合类型,而操作只适用于部分成员时,需要先判断实际情况。若当前位置已经确定为单一类型,直接使用即可。收窄利用类型中已有的信息;如果模型没有表达状态与数据的关联,单靠检查一个状态字段无法补全关系。

用可辨识属性确定成员

每个成员拥有同名属性,且该属性具有不同的字面量类型时,可以检查它来确定完整成员。

ts
type Notice =
  | { kind: "saved"; id: string }
  | { kind: "failed"; error: string };
function describe(n: Notice): string {
  if (n.kind === "saved") return n.id;
  return n.error;
}

进入 saved 分支时,n 被收窄为具有 id 的成员。这个分支返回后,后续位置只能由 failed 成员到达。完整业务模型如何选择,见关联的请求状态实战。

用属性存在性区分结构

联合没有统一的标签,但某属性只存在于其中一种结构时,也可以根据属性存在性判断。本例只使用必填属性。

ts
type Payload = { userId: string } | { percent: number };
function show(p: Payload): string {
  if ("userId" in p) return p.userId.toUpperCase();
  return p.percent.toFixed(0);
}

为什么分支结束后要重新看路径

收窄取决于哪些执行路径能到达当前位置。提前 return 会排除一条路径;仅执行一次打印不会。

ts
function logPayload(p: Payload): void {
  if ("userId" in p) console.log(p.userId);
  // 此处两种成员都可能到达,不能直接访问 p.percent。
  if ("percent" in p) console.log(p.percent);
}

也可以使用 if/else,让每个操作都处在已确认的分支里。不要把规则简化成“if 外一定没有收窄”。事件回调里的外部变量与参数关联问题,放在事件处理器实战中讨论。

官方阅读

  • Narrowing:可辨识联合、属性检查和控制流分析。

这一页,先记到这里。