TypeScript 自定义类型守卫怎么写

在写TypeScript代码时,你是不是遇到过这种情况:明明给函数传入了正确的参数,编译器却总报错,说“无法确认这个值是不是我要的类型”?比如你写了一个处理数据的函数,有时要处理字符串,有时要处理数字,想对传入值做判断,却不知道怎么让编译器“相信”你判断出的类型,只能被迫加一堆繁琐的类型断言,代码又乱又容易出问题。

这时候,TypeScript的自定义类型守卫就能帮上大忙——它能让你亲手告诉编译器:“我对这个值的判断是准确的,你不用再瞎猜了”,让代码既安全又简洁。接下来咱们就一步步搞懂,怎么写好用的自定义类型守卫。

TypeScript 自定义类型守卫怎么写

先搞懂:什么是类型守卫?

其实TypeScript自带了一些简单的类型守卫,比如你写if (typeof value === 'string'),编译器在这个判断块里,会自动把value识别成字符串类型,这就是原生的类型守卫。但原生守卫只能处理基础类型,要是遇到自定义的对象类型,比如你自己写了个User类型,就没法用typeof判断了,这时候就需要自己写“自定义类型守卫”,本质上就是一个返回布尔值的函数,专门用来判断某个值是不是属于你定义的某个类型。

举个生活化的例子:你开了个水果店,要判断收到的货物是不是苹果——原生守卫就像“摸重量超过200g?那是水果,但分不清是不是苹果”;自定义类型守卫就像“看标签:品名是‘红富士’+ 产地是山东 + 无碰伤?那就是苹果”,能精准判断你要的类型。

怎么写基础的自定义类型守卫?

写自定义类型守卫的规则很简单:函数返回值必须是类型谓词,格式是参数名 is 类型,告诉编译器“如果这个函数返回true,那这个参数就是后面这个类型”。

咱们用实际代码举例子:先定义两个自定义对象类型,一个是Cat,一个是Dog,分别有不同的属性:

`typescript

// 自定义两个动物类型

type Cat = { name: string; meow: () => void };

type Dog = { name: string; bark: () => void };

`

现在要写一个函数,判断传入的动物是不是猫,那这个函数就是自定义类型守卫:

`typescript

// 自定义类型守卫:判断是不是猫

function isCat(animal: Cat | Dog): animal is Cat {

// 核心逻辑:判断对象里有没有猫独有的meow方法

return (animal as Cat).meow !== undefined;

}

`

这个函数里的animal is Cat就是类型谓词,当你调用这个函数得到true时,编译器就会自动把这个animal识别成Cat类型,不用你再手动断言。

实际用起来的效果

有了这个守卫,处理动物的函数就变得很简洁,不会再报错了:

`typescript

// 处理动物的函数

function makeSound(animal: Cat | Dog) {

// 用守卫判断:如果是猫,就叫喵

if (isCat(animal)) {

// 这里编译器自动知道animal是Cat,直接调用meow()没问题

animal.meow();

} else {

// 剩下的就是Dog,自动识别,调用bark()

animal.bark();

}

}

`

要是你没写自定义守卫,直接在if里判断(animal as Cat).meow,编译器会一直报错,说“你可能判断错了”,加了自定义守卫后,编译器就完全信任你的判断了。

写自定义类型守卫的注意事项

  • 类型谓词必须是函数的第一个参数,不能是嵌套属性,比如不能写animal.name is 'Kitty'这种,只能是函数的入参名。
  • 逻辑一定要准确:守卫的判断条件必须和你要判断的类型严格对应,不然编译器信任你后,实际值不对就会出运行时bug,比如你判断是不是猫,就必须检查猫独有的属性,不能用“名字里有‘小’”这种模糊条件。
  • 不要过度使用:如果原生类型守卫(typeof、instanceof)能搞定的,就不用写自定义守卫,避免代码冗余。

掌握自定义类型守卫后,你就能精准控制TypeScript的类型推断,不用再和编译器反复“解释”你的判断,写出来的代码既安全又清爽,还能大幅减少类型断言的冗余代码。

上一篇 TypeScript 如何做类型测试