Skip to content

类型守卫

1. 类型查询操作符typeof

TypeScript 存在两种功能不同的 typeof 操作符。我们最常见的一种 typeof 操作符就是 JavaScript 中,用于检查变量类型的 typeof ,它会返回 "string" / "number" / "object" / "undefined" 等值。而除此以外, TypeScript 还新增了用于类型查询的 typeof ,,这个 typeof 返回的是一个 TypeScript 类型:

ts
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 语句等),那河流流过这里就会收集对应的信息,知道所有的类型信息收集完毕

提示

类型守卫一般用于联合类型中去判断更加具体的类型

ts
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到单独的函数中:

ts
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具体的类型:

类型谓词

  1. 格式: function isXXX (v:unknow): val is 类型 { }
  2. 类型谓词用于函数中,告诉类型系统这个函数体内的逻辑返回true时他的的具体返回值类型
ts
/**
 * 此时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
}

除了使用简单的原始类型以外,还可以在类型守卫中使用对象类型、联合类型等:

ts
 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)