Skip to content

is 工具集:精准的类型判断与类型收窄

在 JavaScript 的动态类型世界中,运行时类型检查是保障程序健壮性的基石。typeof 虽然简单,但面对 null、数组、日期等特殊值时常常力不从心。

is 工具集通过一系列 isXXX 函数(如 isString, isObject, isArray),提供了一套精准、可靠、类型安全的类型判断方案。更重要的是,结合 类型谓词(Type Predicate),它能让 TypeScript 编译器在条件分支中自动收窄类型,实现更智能的代码提示与错误检查。

为何需要 is 工具集?typeof 的局限

typeof 的常见陷阱

ts
typeof null        // "object" 
typeof []          // "object" 
typeof new Date()  // "object" 
typeof /regex/     // "object"

typeof 无法区分对象的具体类型,导致判断逻辑复杂且易错。

传统修复方案

ts
// 判断数组
Array.isArray(value) // 推荐

// 判断 null
value === null

// 判断日期
value instanceof Date

但这些方法零散,缺乏统一性。

核心实现:Object.prototype.toString.call()

唯一可靠的“万能”检测法

ts
const toString = Object.prototype.toString

toString.call(null)        // "[object Null]"
toString.call([])          // "[object Array]"
toString.call(new Date())  // "[object Date]"
toString.call(/regex/)     // "[object RegExp]"
toString.call('hello')     // "[object String]"

toString.call() 能返回对象的内部 [[Class]] 标签,不受原型链影响,是最可靠的类型检测方式。

实现 is 函数族

ts
const isType = <T>(type: string) => (value: unknown): value is T =>
  Object.prototype.toString.call(value) === `[object ${type}]`

export const isString = isType<string>('String')
export const isNumber = isType<number>('Number')
export const isBoolean = isType<boolean>('Boolean')
export const isArray = isType<unknown[]>('Array')
export const isFunction = isType<Function>('Function')
export const isDate = isType<Date>('Date')
export const isRegExp = isType<RegExp>('RegExp')
export const isObject = isType<object>('Object')

关键点

  • 使用工厂函数 isType 避免重复代码。
  • 返回值类型为 value is T,即类型谓词

空值判断:isEmpty, isNil, isUndefined

isNilnullundefined

ts
const isNil = (value: unknown): value is null | undefined =>
  value == null // 利用 == 的宽松相等

常用于判断值是否存在。

isUndefined:严格判断 undefined

ts
const isUndefined = (value: unknown): value is undefined =>
  value === undefined

适用于需要区分 undefinednull 的场景。

isEmpty:空数组、空对象、空字符串等

ts
const isEmpty = (value: unknown): boolean => {
  if (value == null) return true
  if (isArray(value)) return value.length === 0
  if (isString(value)) return value === ''
  if (isObject(value)) return Object.keys(value).length === 0
  return false
}

注意:isEmpty 不返回类型谓词,因为它不指向单一类型。

类型谓词(Type Predicate):让 TypeScript “智能”起来

什么是类型谓词?

value is T 是 TypeScript 的特殊语法,表示: “如果此函数返回 true,则 value 的类型可以被收窄为 T。”

实战:条件分支中的类型收窄

ts
const processData = (input: string | string[] | null) => {
  if (isString(input)) {
    // TypeScript 知道 input 是 string
    console.log(input.toUpperCase())
    // input.push('x') // 编译错误:Property 'push' does not exist
  } else if (isArray(input)) {
    // TypeScript 知道 input 是 string[]
    console.log(input.length)
  } else {
    // TypeScript 知道 input 是 null
    console.log('No data')
  }
}

没有类型谓词时,inputif 块内仍是联合类型,需强制断言。

类型谓词 vs 类型断言

ts
// 不安全:可能出错
if (typeof input === 'string') {
  console.log((input as string).toUpperCase())
}

// 安全:编译器自动收窄
if (isString(input)) {
  console.log(input.toUpperCase())
}

类型谓词是编译时证明,而断言是运行时假设

实战:在 deepGet 中安全使用 is

ts
const deepGet = <T, D = undefined>(
  obj: T,
  path: string | string[],
  defaultValue?: D
): unknown => {
  if (!isObject(obj)) return defaultValue // isObject 收窄类型

  const paths = isArray(path) ? path : path.split('.')
  let current: any = obj

  for (const key of paths) {
    if (!isObject(current)) return defaultValue // 收窄后可安全访问
    current = current[key]
  }

  return current !== undefined ? current : defaultValue
}

isObject 不仅判断类型,更让 current[key] 的访问变得类型安全。

高级技巧:自定义类型谓词

你可以为任意复杂条件创建类型谓词:

ts
interface User {
  name: string
  age: number
}

const isActiveUser = (user: unknown): user is User =>
  isObject(user) &&
  isString(user.name) &&
  isNumber(user.age) &&
  user.age >= 18

// 使用
if (isActiveUser(data)) {
  // data 被收窄为 User 类型
  console.log(`Welcome, ${data.name}!`)
}

这在处理 API 响应校验时极为有用。

结语:is 工具集是类型安全的守护者

is 工具集的价值远不止于“判断类型”:

  • 精准toString.call() 避开 typeof 陷阱。
  • 统一:一套 API 处理所有类型。
  • 安全:类型谓词实现编译时验证。
  • 智能:让 IDE 在 if 分支中提供精确补全。

当你使用 isString(x) 而非 typeof x === 'string' 时,你不仅在写代码,更在为 TypeScript 编译器提供推理线索,构建一个从运行时到编译时的完整类型防护网。

在动态类型的 JavaScript 中,is 工具集是通往静态类型安全世界的桥梁。