将类型推导为更精确类型的过程

收窄方法

typeof

使用 typeof 判断变量类型进行收窄

function padLeft(padding: number | string, input: string) {
  if (typeof padding === "number") { // 收窄成 number
    return " ".repeat(padding) + input
  }
  return padding + input
}

真值收窄(Truthiness narrowing)

if 条件语句会自动将结果转换为 boolean 类型,也可以使用 Boolean() 函数强制转为 boolean 值,或者使用更加简短的 !!

function printAll(strs: string | string[] | null) {
  if (strs && typeof strs === "object") { // 收窄成 string[]
    for (const s of strs) {
      console.log(s)
    }
  } else if (typeof strs === "string") { // 收窄成 string
    console.log(strs)
  }
}

等值收窄(Truthiness narrowing)

使用 switch 语句或等值检查比如 === !== == != 去收窄类型

function printAll(strs: string | string[] | null) {
  if (strs !== null) { // 收窄成 string | string[]
    if (typeof strs === "object") { // 收窄成 string[]
      for (const s of strs) {
        console.log(s)
      }
    } else if (typeof strs === "string") { // 收窄成 string
      console.log(strs)
    }
  }
}

in

判断一个对象是否有对应的属性名

type Fish = { swim: () => void };
type Bird = { fly: () => void };
 
function move(animal: Fish | Bird) {
  if ("swim" in animal) { // 收窄成 Fish
    return animal.swim()
  }
 
  return animal.fly()
}

instanceof

function logValue(x: Date | string) {
  if (x instanceof Date) { // 收窄成 Date
    console.log(x.toUTCString())
  } else {
    console.log(x.toUpperCase())
  }
}

可辨别联合(Discriminated unions)

当联合类型中的每个类型,都包含了一个共同的字面量类型的属性(也叫可辩别属性),那么就可以根据这个可辩别属性的值来区分出不同的类型

interface Circle {
  kind: "circle"
  radius: number
}
 
interface Square {
  kind: "square"
  sideLength: number
}
 
type Shape = Circle | Square
 
 
function getArea(shape: Shape) {
  switch (shape.kind) { // 只需要判断共有的属性就可以收窄
    case "circle": // 收窄为 Circle
      return Math.PI * shape.radius ** 2
    case "square": // 收窄为 Square
      return shape.sideLength ** 2
  }
}