FumadocsZDecode
Typescript

类型守卫函数

类型守卫函数(Type Predicate Function)通过返回值 param is Type 告诉 TypeScript:如果函数返回 true,则 param 在该分支内是指定类型。

function isString(value: unknown): value is string {
  return typeof value === 'string'
}

调用后,TypeScript 在 if 分支内自动收窄类型:

function print(value: string | number) {
  if (isString(value)) {
    console.log(value.toUpperCase()) // value: string
  } else {
    console.log(value.toFixed(2))    // value: number
  }
}

与类型断言的区别

as 断言是强制告诉编译器"我知道它是什么类型",运行时不做任何检查,断言错误只会在运行时报错。

// 断言:跳过检查,不安全
const len = (value as string).length // 如果 value 不是 string,运行时出错

// 类型守卫:先验证,再使用,安全
if (isString(value)) {
  const len = value.length // TypeScript 确认 value 是 string
}

常用模式

检查原始类型

function isNumber(value: unknown): value is number {
  return typeof value === 'number' && !Number.isNaN(value)
}

function isString(value: unknown): value is string {
  return typeof value === 'string'
}

检查类实例

function isDate(value: unknown): value is Date {
  return value instanceof Date
}

检查联合类型中的具体成员

通过判断某个特有属性是否存在来区分联合类型的成员:

interface Circle {
  kind: 'circle'
  radius: number
}

interface Rectangle {
  kind: 'rect'
  width: number
  height: number
}

type Shape = Circle | Rectangle

function isCircle(shape: Shape): shape is Circle {
  return shape.kind === 'circle'
}

function getArea(shape: Shape) {
  if (isCircle(shape)) {
    return Math.PI * shape.radius ** 2  // shape: Circle
  }
  return shape.width * shape.height     // shape: Rectangle
}

过滤数组

类型守卫函数可以直接传给 Array.filter,过滤后的数组类型自动收窄:

const values: (string | null | undefined)[] = ['a', null, 'b', undefined, 'c']

function isString(v: string | null | undefined): v is string {
  return typeof v === 'string'
}

const strings = values.filter(isString)
// strings: string[],而非 (string | null | undefined)[]

不使用类型守卫时,filter 无法收窄类型:

// ❌ 类型仍为 (string | null | undefined)[]
const strings = values.filter(v => v !== null && v !== undefined)

验证外部数据结构

处理 API 响应或 JSON 数据时,用类型守卫替代断言:

interface User {
  id: number
  name: string
}

function isUser(data: unknown): data is User {
  return (
    typeof data === 'object' &&
    data !== null &&
    typeof (data as User).id === 'number' &&
    typeof (data as User).name === 'string'
  )
}

async function fetchUser(id: number) {
  const res = await fetch(`/api/users/${id}`)
  const data: unknown = await res.json()

  if (isUser(data)) {
    console.log(data.name) // data: User
  } else {
    throw new Error('Invalid user data')
  }
}

On this page