合普知识库
柔彩主题三 · 更轻盈的阅读体验

TypeScript类型守卫:让代码更安全的小技巧

发布时间:2025-12-15 09:12:02 阅读:445 次

在写前端项目时,经常会遇到一个变量可能是多种型的情况。比如从接口返回的数据,某个字段可能是字符串,也可能是对象,甚至可能是 null。这时候如果不小心处理,运行时就容易出错。

TypeScript 虽然能帮我们提前发现一些类型问题,但有时候类型太宽泛,光靠静态检查还不够。这时候就需要“类型守卫”来帮忙,在运行时确认具体类型,让后续操作更安全

什么是类型守卫

类型守卫其实就是一个函数或表达式,它的作用是告诉 TypeScript:“放心,我现在确定这个值是什么类型”。一旦通过了这个“检查”,TS 就会在接下来的代码块里按对应的类型来处理。

最常见的例子是联合类型的判断。比如有个函数接收 number 或 string 类型的参数,你想对它们分别处理:

function printValue(value: number | string) {
  if (typeof value === 'string') {
    // 这里 TypeScript 知道 value 一定是 string
    console.log(value.toUpperCase());
  } else {
    // 这里一定是 number
    console.log(value.toFixed(2));
  }
}

这里的 typeof value === 'string' 就是一个类型守卫。TypeScript 能识别这种常见的判断方式,并自动缩小类型范围。

自定义类型守卫函数

有些情况下,typeof 不够用。比如你面对的是多个对象类型组成的联合类型,就得自己写判断逻辑。

假设你在做一个表单校验功能,有两种数据结构:用户信息和订单信息。你可以这样定义:

interface User {
  name: string;
  age: number;
}

interface Order {
  orderId: string;
  amount: number;
}

type FormData = User | Order;

现在要写个函数判断到底是哪种类型。可以定义一个类型守卫函数:

function isUser(data: FormData): data is User {
  return (data as User).name !== undefined;
}

注意返回值写法:data is User,这是关键。它告诉编译器:如果这个函数返回 true,那 data 就一定是 User 类型。

使用起来就很顺手:

function handleFormData(data: FormData) {
  if (isUser(data)) {
    // 此时可以直接访问 name 和 age
    console.log('欢迎你,' + data.name);
  } else {
    // 反之就是 Order
    console.log('订单金额:' + data.amount);
  }
}

用 in 操作符做类型区分

除了 typeof 和自定义函数,还可以用 in 操作符来判断属性是否存在,这也是一种类型守卫。

function process(input: User | Order) {
  if ('name' in input) {
    console.log('这是一个用户:', input.name);
  } else {
    console.log('这是一个订单:', input.orderId);
  }
}

这种写法简洁直观,适合区分具有不同字段的对象。

实战小场景

想象你在做一个记账小程序,从本地存储读取数据时,可能拿到旧格式(只有金额)或新格式(带分类)。你可以用类型守卫来兼容两种结构:

interface OldRecord {
  money: number;
}

interface NewRecord {
  money: number;
  category: string;
}

function isNewRecord(r: any): r is NewRecord {
  return 'category' in r;
}

// 使用
const record: OldRecord | NewRecord = JSON.parse(localStorage.getItem('last')); 
if (isNewRecord(record)) {
  showWithCategory(record.category, record.money);
} else {
  showSimple(record.money);
}

这样即使数据来源不可控,也能保证程序不会因为访问不存在的字段而崩溃。