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,通常只会让代码更绕。

相关推荐
zhensherlock2 小时前
Protocol Launcher 系列:一键唤起 Windsurf 智能 IDE
javascript·ide·vscode·ai·typescript·github·ai编程
进击切图仔2 小时前
生成 .so 和使用 .so
java·javascript·算法
SuperEugene2 小时前
Vant 4 实战教程:Vue3 移动端后台管理系统从选型到开发|Vue生态精选篇
前端·javascript·vue.js·前端框架·vant
xuankuxiaoyao2 小时前
VUE.JS 实践 第一章
前端·javascript·vue.js
夫唯不争,故无尤也2 小时前
原始文档元数据metadata
java·前端·javascript·sql
We་ct2 小时前
LeetCode 211. 添加与搜索单词 - 数据结构设计:字典树+DFS解法详解
开发语言·前端·数据结构·算法·leetcode·typescript·深度优先
我命由我123453 小时前
前端开发 - this 指向问题(直接调用函数、对象方法、类方法)
开发语言·前端·javascript·vue.js·react.js·html5·js
低保和光头哪个先来3 小时前
TinyEditor 篇1:实现工具栏按钮向服务器上传图片
服务器·开发语言·前端·javascript·vue.js·前端框架
还是大剑师兰特3 小时前
vue3+vite+JS,使用Echarts封装一个饼图,父子组件联动
javascript·vue.js·echarts