TypeScript 里的 Type Guard 是什么

这篇写给前端新手。

如果你刚接触 TypeScript,看到下面这种写法时,八成会懵:

ts 复制代码
(item): item is SomeType => Boolean(item.id)

这东西叫 type guard。中文可以理解成"类型守卫"或者"类型保护"。

说人话就是:

它是一个告诉 TypeScript 的方法:

"如果这个判断通过了,你就可以把这个值当成更具体的类型来用。"

先理解一个核心问题

TypeScript 经常会遇到这种情况:

ts 复制代码
type User = {
  id?: string;
  name?: string;
};

const list: User[] = [
  { id: '1', name: 'A' },
  { name: 'B' },
];

这里 idname 后面有 ?,意思是它们可能不存在。

所以 TypeScript 会很谨慎。哪怕你心里知道某个值大概率有 id,它也不会随便相信你。

没有 type guard 时会怎样

ts 复制代码
const validUsers = list.filter((item) => Boolean(item.id));

这行代码在运行时没问题,它确实会把没有 id 的项过滤掉。

但是 TypeScript 往往仍然认为:

ts 复制代码
validUsers[0].id

还是"可能是 undefined"。

为什么?

因为它只看到你写了一个普通布尔判断,它不一定能完全推断出:

"过滤完以后,这个数组里的每一项都一定有 id。"

type guard 是怎么帮忙的

这时候可以这样写:

ts 复制代码
const validUsers = list.filter(
  (item): item is { id: string; name?: string } => Boolean(item.id)
);

这里最重要的是:

ts 复制代码
item is { id: string; name?: string }

这句话是对 TypeScript 说的,不是给浏览器看的。

它的意思是:

"如果这个函数返回 true,那 item 就可以被当成一个 id 一定存在的对象。"

这样后面你再访问:

ts 复制代码
validUsers[0].id

TypeScript 就会更放心,因为它知道这里的 id 已经被"缩窄"成 string 了。

它和普通判断有什么区别

看下面这两部分:

ts 复制代码
Boolean(item.id)

这是运行时判断。程序真正执行的是它。

ts 复制代码
item is { id: string; name?: string }

这是类型提示。它主要是写给 TypeScript 编译器看的。

所以可以把一整句拆开理解:

  • 前半句负责"真的检查"
  • 后半句负责"告诉 TypeScript 检查完以后该怎么看这个值"

什么时候 type guard 很有用

最常见是下面这几类情况:

  1. 一个字段是可选的,你过滤后想让 TypeScript 知道它现在一定存在。
  2. 一个值可能是多种类型中的一种,你想先判断再安全使用。
  3. 你在 filterfindif 里做了检查,但 TypeScript 还不够确定。

比如:

ts 复制代码
type Data = string | number;

function printLength(value: Data) {
  if (typeof value === 'string') {
    console.log(value.length);
  }
}

这里 typeof value === 'string' 其实也是一种 type guard。

它告诉 TypeScript:

"进到这个 if 里面后,value 就是字符串。"

什么时候它是多余的

如果一个值本来就已经是那个类型了,那再写一遍 type guard 就没有太大意义。

比如:

ts 复制代码
type Category = {
  frameworkId: string;
  categoryId: string;
};

const categories: Category[] = [];

这里数组里的每一项本来就是:

ts 复制代码
{
  frameworkId: string;
  categoryId: string;
}

那你再写:

ts 复制代码
(item): item is Category => Boolean(item.frameworkId && item.categoryId)

就有点像在说:

"如果判断通过,那它就是它自己。"

这时 reviewer 往往会觉得这段写法太重了,因为它没有提供新的类型信息。

一句最实用的判断标准

你可以这样问自己:

"我写这个 type guard 之后,TypeScript 知道的类型信息,比之前更多了吗?"

如果答案是:

  • 是,那它通常有价值
  • 不是,那它大概率只是写复杂了

新手可以先记住这三种

1. typeof

ts 复制代码
if (typeof value === 'string') {
  // 这里 value 是 string
}

2. instanceof

ts 复制代码
if (error instanceof Error) {
  // 这里 error 是 Error
}

3. 自定义 item is Xxx

ts 复制代码
const result = list.filter(
  (item): item is { id: string } => Boolean(item.id)
);

这个最灵活,但也是最容易被滥用的。

最后一句总结

type guard 不是为了让代码"看起来更高级",而是为了在类型不够明确的时候,帮 TypeScript 正确缩窄类型。

如果原来的类型已经很明确,再加一个 type guard,通常只会让代码更绕。

相关推荐
aq55356005 小时前
Laravel5.X核心特性全解析
ubuntu·debian
幺风6 小时前
Claude Code 源码分析 — Tool/MCP/Skill 可扩展工具系统
前端·javascript·ai编程
ID_180079054736 小时前
淘宝 API 上货 / 商品搬家 业务场景实现 + JSON 返回示例
前端·javascript·json
M ? A6 小时前
Vue 动态组件在 React 中,VuReact 会如何实现?
前端·javascript·vue.js·经验分享·react.js·面试·vureact
深圳市九鼎创展科技7 小时前
MT8883 vs RK3588 开发板全面对比:选型与场景落地指南
大数据·linux·人工智能·嵌入式硬件·ubuntu
闫利朋7 小时前
Ubuntu22.04桌面版安装Cubic2024版本并定制ISO完整教程
ubuntu·自动化的安装系统
竹林8188 小时前
RainbowKit快速集成多链钱包连接,我如何从“连不上”到“丝滑切换”
前端·javascript
No8g攻城狮8 小时前
【前端】Vue 中 const、var、let 的区别
前端·javascript·vue.js
fishmemory7sec9 小时前
Vue大屏自适应容器组件:v-scale-screen
前端·javascript·vue.js
饺子不吃醋9 小时前
Promise原理、手写与 async、await
前端·javascript