类型守卫
1. 类型查询操作符typeof
TypeScript
存在两种功能不同的 typeof
操作符。我们最常见的一种 typeof
操作符就是 JavaScript
中,用于检查变量类型的 typeof
,它会返回 "string
" / "number
" / "object
" / "undefined
" 等值。而除此以外, TypeScript
还新增了用于类型查询的 typeof
,,这个 typeof
返回的是一个 TypeScript
类型:
const name = '给我一个div'
const age = 999
const p = {
name: 'zkp',
age: 999
}
type n = typeof name // '给我一个div' 字面量类型
type a = typeof age // 999
type p = typeof p // { name: string age: number}
提示
绝大部分情况下,typeof
返回的类型就是当你把鼠标悬浮在变量名上时出现的推导后的类型,并且是最窄的推导程度(即到字面量类型的级别)
2. 类型守卫
TypeScript
中提供了非常强大的类型推导能力,它会随着你的代码逻辑不断尝试收窄类型,这一能力称之为类型的控制流分析(也可以简单理解为类型推导)可以想象有一条河流,它从上而下流过你的程序,随着代码的分支分出一条条支流,在最后重新合并为一条完整的河流。在河流流动的过程中,如果遇到了有特定条件才能进入的河道(比如if else
语句、switch case
语句等),那河流流过这里就会收集对应的信息,知道所有的类型信息收集完毕
提示
类型守卫一般用于联合类型中去判断更加具体的类型
function fn(val: string | number | boolean) {
if (typeof val === 'string') {
// 类型系统在这里确定val是string类型 所以val调用string的方法会提示
console.log(val.slice(0, 1))
}
else if (typeof val === 'number') {
// 确定val是number类型
console.log(val.toExponential(2))
}
else {
// 确定val是boolean类型
console.log(val)
}
}
以上代码通过if else
语句帮助类型系统确定不同分支val
参数的类型,同时类型系统也反过来保护逻辑代码的稳定与正确
2.1 类型谓词
上例中的代码,在每个流程语句中都要判断参数val
的类型,可以提取判断类型的代码typeof val === xxx
到单独的函数中:
function isString(v: unknown): boolean {
if (typeof v === 'string')
return true
else return false
}
function fn(val: string | number) {
if (isString(val))
console.log(val.slice(0, 1)) 类型“string | number”上不存在属性“slice”。
else
console.log(val.toFixed(2))类型“string | number”上不存在属性“toFixed”
}
此时类型系统就不能判断val
的具体类型了,因为isString
函数只是告诉类型系统他的返回值为真或者假,并没有告诉他val
是什么具体的类型,这里的类型控制流分析做不到跨函数上下文来进行类型的信息收集,可以使用类型谓词来告诉typeScript
具体的类型:
类型谓词
- 格式:
function isXXX (v:unknow): val is 类型 { }
- 类型谓词用于函数中,告诉类型系统这个函数体内的逻辑返回
true
时他的的具体返回值类型
/**
* 此时isString函数不止返回boolean,并且能告诉类型系统正确的类型
*/
function isString(v: unknown): v is string {
if (typeof v === 'string')
return true
else return false
}
function fn(val: string | number) {
if (isString(val))
console.log(val.slice(0, 1)) // val: string
else
console.log(val.toFixed(2))// val: number
}
除了使用简单的原始类型以外,还可以在类型守卫中使用对象类型、联合类型等:
type Falsy = false | '' | 0 | null | undefined
// 是否是假值
const isFalsy = (val: unknown): val is Falsy => !val
// 是否是原始类型 不包括不常用的 symbol 和 bigint
const types = ['string', 'number', 'boolean', 'undefined']
type Primitive = string | number | boolean | undefined
const isPrimitive = (val: unknown): val is Primitive => types.includes(typeof val)