类型守卫

类型守卫是在运行时检查类型的表达式,帮助 TypeScript 在特定代码块中确定变量的具体类型。类型守卫让我们可以安全地访问联合类型中特定类型的属性和方法。

typeof 类型守卫

typeof 操作符用于检查原始类型。

function format(value: string | number): string {
  if (typeof value === "string") {
    return value.toUpperCase()
  }
  return value.toFixed(2)
}

console.log(format("hello"))
console.log(format(3.14159))

typeof 可以识别 stringnumberbooleansymbolundefinedfunctionobject 类型。

instanceof 类型守卫

instanceof 操作符用于检查类实例。

class Dog {
  bark(): void {
    console.log("汪汪叫")
  }
}

class Cat {
  meow(): void {
    console.log("喵喵叫")
  }
}

function makeSound(animal: Dog | Cat): void {
  if (animal instanceof Dog) {
    animal.bark()
  } else {
    animal.meow()
  }
}

const dog = new Dog()
const cat = new Cat()

makeSound(dog)
makeSound(cat)

instanceof 检查对象是否是某个类的实例,适用于类类型。

in 操作符类型守卫

in 操作符检查对象是否有某个属性。

interface Bird {
  fly(): void
  layEggs(): void
}

interface Fish {
  swim(): void
  layEggs(): void
}

function move(animal: Bird | Fish): void {
  if ("fly" in animal) {
    animal.fly()
  } else {
    animal.swim()
  }
  animal.layEggs()
}

in 操作符检查属性是否存在,适用于区分有不同属性的对象类型。

自定义类型守卫

可以创建自定义的类型守卫函数。

interface Fish {
  swim: () => void
}

interface Bird {
  fly: () => void
}

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined
}

function move(pet: Fish | Bird): void {
  if (isFish(pet)) {
    pet.swim()
  } else {
    pet.fly()
  }
}

pet is Fish 是类型谓词,告诉 TypeScript 如果函数返回 true,则 petFish 类型。

类型谓词

类型谓词语法:parameterName is Type

interface Admin {
  name: string
  permissions: string[]
}

interface User {
  name: string
  email: string
}

function isAdmin(user: Admin | User): user is Admin {
  return (user as Admin).permissions !== undefined
}

function processUser(user: Admin | User): void {
  if (isAdmin(user)) {
    console.log(`管理员权限: ${user.permissions.join(", ")}`)
  } else {
    console.log(`用户邮箱: ${user.email}`)
  }
}

类型谓词让类型守卫更加语义化和可复用。

null 检查

类型守卫常用于检查 null 和 undefined。

function process(value: string | null): string {
  if (value === null) {
    return "空值"
  }
  return value.toUpperCase()
}

console.log(process("hello"))
console.log(process(null))

直接比较 === null=== undefined 可以缩小类型范围。

可选链与空值合并

可选链和空值合并运算符也可以用于处理 null 和 undefined。

interface User {
  name: string
  address?: {
    city: string
  }
}

function getCity(user: User): string {
  return user.address?.city ?? "未知城市"
}

console.log(getCity({ name: "张三", address: { city: "北京" } }))
console.log(getCity({ name: "李四" }))

?. 安全访问可能为 null 或 undefined 的属性,?? 提供默认值。

类型断言与类型守卫

类型断言可以强制指定类型,但不进行运行时检查。

interface User {
  name: string
  age: number
}

function process(value: unknown): void {
  const user = value as User
  console.log(user.name)
}

process({ name: "张三", age: 25 })

类型断言不安全,应该优先使用类型守卫。

实际应用

类型守卫在实际开发中常用于处理 API 响应。

interface SuccessResponse {
  status: "success"
  data: {
    id: number
    name: string
  }
}

interface ErrorResponse {
  status: "error"
  error: string
}

type ApiResponse = SuccessResponse | ErrorResponse

function isSuccess(response: ApiResponse): response is SuccessResponse {
  return response.status === "success"
}

function handleResponse(response: ApiResponse): void {
  if (isSuccess(response)) {
    console.log(`成功: ${response.data.name}`)
  } else {
    console.log(`错误: ${response.error}`)
  }
}

handleResponse({
  status: "success",
  data: { id: 1, name: "张三" }
})

handleResponse({
  status: "error",
  error: "网络错误"
})

自定义类型守卫让代码更加清晰和可维护。

小结

类型守卫是在运行时检查类型的表达式,帮助 TypeScript 确定变量的具体类型。typeof 用于原始类型,instanceof 用于类实例,in 操作符用于检查属性。自定义类型守卫使用类型谓词语法,可以创建可复用的类型检查函数。合理使用类型守卫可以让代码更加安全和可读。