我是靠谱客的博主 长情老鼠,这篇文章主要介绍TypeScript中类型守卫Type Guard的介绍和使用,现在分享给大家,希望可以做个参考。

Type Guard不是一种类型,而是一种能够确认具体类型的一种机制,如针对union类型经常设置一个type字段来作为当前类型的唯一标识,从而在使用时能够正确识别:

复制代码
1
2
3
4
5
6
7
8
9
type Contact = { type: 'email'; email: string; } | { type: 'phone'; phone: string; } function saveContact(contact: Contact) { if (contact.type === 'email') { // 这里能够确定类型是 { type: 'email'; email: string; },能够访问contact.email } else { // 这里能够确定类型是 { type: 'phone'; phone: string; },能够访问contact.phone } }

在开发过程中,我们可能都不自觉地使用了下面的一些方式来确定当前访问数据的类型,其实它们也是Type Guard

空值校验

复制代码
1
2
3
4
5
6
7
8
9
function hello(name?: string) { if (name) { // 这里能确定name是string类型 console.log(`Hello, ${name.toUpperCase()}`) } else { console.log('Hello') } }

typeof

使用typeof也能确定类型,不过只能用于js的基本数据类型(null除外),而不能用于interfacetype定义的类型,因为在运行时这些类型就不在了:

复制代码
1
2
3
4
5
6
7
8
function setValue(value: number | string) { if (typeof value === 'number') { return value.toFixed(2) } else { return parseInt(value).toFixed(2) } }

instanceof

用于校验类,和interfacetype不同的是,类的类型信息会在运行时保留,所以可以用instanceof作校验:

复制代码
1
2
3
4
5
6
7
8
9
class Person { constructor(public name: string, public age: string) {} } function logPerson(obj: any) { if (obj instanceof Person) { console.log(`${obj.name} is ${obj.age} years old`) } }

自定义Type Guard

TypeScript中也可以自定义Type Guard,所谓自定义Type Guard就是一个返回boolean值的函数,此函数可以对函数的参数进行断言校验:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import axios, { AxiosResponse } from 'axios' interface Person { name: string; age: number; } function isPerson(obj: any): obj is Person { return 'name' in obj && 'age' in obj } axios.get('/v1/api/test',) .then((res: AxiosResponse) => res.data) .then((data: unknown) => { if (isPerson(data)) { // 通过自定义Type Guard,可以断定此处data是Person类型 console.log(`${data.name.toUpperCase()} is ${data.age} years old`) } })

自定义Type Guard常用于未知的外部的数据类型校验,如从后端返回的数据,因为TypeScript不会侵入运行时环境,所以TypeScript在这种外部数据的情况下是无法做到类型约束的,所以TypeScript不得不信任我们提供的类型,而我们就可以利用自定义Type Guard提供一个类型断言,当数据满足我们提供的校验函数时,就可以数据作为我们提供的类型进行处理了,而且这个校验函数能够在运行时工作。但是要注意此时就需要我们保证校验函数的严谨性及具体的数据的正确性了,比如上面我们断定了data是Person类型,所以我们当data.name是string类型,所以能够调用toUpperCase方法,但是如果后端返回的值是{ name: 12, age: 22 },也能通过isPerson的校验,但是调用toUpperCase就会报错。此时我们可以再细化一下isPerson的实现:

复制代码
1
2
3
4
function isPerson(obj: any): obj is Person { return 'name' in obj && typeof obj.name === 'string' && 'age' in obj && typeof obj.age === 'number' }

TypeScript提供了Type Guard能够对外部数据做类型断言的能力,但需要自己实现其中的校验逻辑,所以要考虑校验函数的有效性、严谨性及效率。

实用场景

考虑如下代码:

复制代码
1
2
3
4
5
6
7
8
9
type Person = { name: string age?: number } // 获得所有age属性 function getPersonAges(persons: Person[]): number[] { return persons.filter(person => person.age !== undefined).map(person => person.age) }

但是上面的代码却会报错:

复制代码
1
2
3
4
Type '(number | undefined)[]' is not assignable to type 'number[]'. Type 'number | undefined' is not assignable to type 'number'. Type 'undefined' is not assignable to type 'number'.

虽然我们在逻辑上的处理上是没错的,但是TypeScript的角度上来说报错也是理所当然的:

使用filter处理得到的结果类型仍然是Person[],到达mapPerson类型的数据取值age自然会得到number | undefined类型,因为默认情况下我们使用的Array.filter的函数签名是这样的:

复制代码
1
2
3
// lib.es5.d.ts filter(callbackfn: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[]

很显然,此时我们的数组为T[]类型,得到的结果也肯定是T[]类型的。

那有什么方法能够解决上面的错误呢?实际上Array.filter还有另一种利用了Type Guard的函数签名:

复制代码
1
2
3
// lib.es5.d.ts filter<S extends T>(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]

在此种情况下,我们首先需要提供一个类型T的子类型S,然后回调函数需要提供一个Type Guard的断言函数,用于校验当前处理的值是否为S类型,抛弃掉不满足S类型的值,从而使得返回值的类型为S[]。使用此方式重写上面的例子:

复制代码
1
2
3
4
5
6
7
8
9
10
11
type Person = { name: string age?: number } type FullPerson = Required<Person> function getPersonAges(persons: Person[]): number[] { return persons .filter<FullPerson>((person): person is FullPerson => person.age !== undefined) .map(person => person.age); }

这样经过filter处理后得到的结果类型为FullPerson[],到达mapFullPerson类型的数据取值age就能得到我们想要的number类型数据了。

总结

自定义Type Guard需要开发者提供断言函数,提供符合某类型的校验实现。断言函数和普通的函数定义类似,只是在函数返回值的签名处有所差异:普通函数返回值的签名是一个具体的类型,而断言函数返回值的签名需要是一个断言

复制代码
1
2
3
4
5
6
7
8
9
// 普通函数 function isPerson(obj: any): boolean { // 具体实现,需要返回一个boolean值 } // Type Guard断言函数 function isPerson(obj: any): obj is Person { // 具体实现,返回true表示obj经过我们验证是Person类型,返回false表示obj经过我们验证不是Person类型 }

最后

以上就是长情老鼠最近收集整理的关于TypeScript中类型守卫Type Guard的介绍和使用的全部内容,更多相关TypeScript中类型守卫Type内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(67)

评论列表共有 0 条评论

立即
投稿
返回
顶部