is 工具集:精准的类型判断与类型收窄
在 JavaScript 的动态类型世界中,运行时类型检查是保障程序健壮性的基石。typeof 虽然简单,但面对 null、数组、日期等特殊值时常常力不从心。
is 工具集通过一系列 isXXX 函数(如 isString, isObject, isArray),提供了一套精准、可靠、类型安全的类型判断方案。更重要的是,结合 类型谓词(Type Predicate),它能让 TypeScript 编译器在条件分支中自动收窄类型,实现更智能的代码提示与错误检查。
为何需要 is 工具集?typeof 的局限
typeof 的常见陷阱
typeof null // "object"
typeof [] // "object"
typeof new Date() // "object"
typeof /regex/ // "object"typeof 无法区分对象的具体类型,导致判断逻辑复杂且易错。
传统修复方案
// 判断数组
Array.isArray(value) // 推荐
// 判断 null
value === null
// 判断日期
value instanceof Date但这些方法零散,缺乏统一性。
核心实现:Object.prototype.toString.call()
唯一可靠的“万能”检测法
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 函数族
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
isNil:null 或 undefined
const isNil = (value: unknown): value is null | undefined =>
value == null // 利用 == 的宽松相等常用于判断值是否存在。
isUndefined:严格判断 undefined
const isUndefined = (value: unknown): value is undefined =>
value === undefined适用于需要区分 undefined 和 null 的场景。
isEmpty:空数组、空对象、空字符串等
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。”
实战:条件分支中的类型收窄
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')
}
}没有类型谓词时,input 在 if 块内仍是联合类型,需强制断言。
类型谓词 vs 类型断言
// 不安全:可能出错
if (typeof input === 'string') {
console.log((input as string).toUpperCase())
}
// 安全:编译器自动收窄
if (isString(input)) {
console.log(input.toUpperCase())
}类型谓词是编译时证明,而断言是运行时假设。
实战:在 deepGet 中安全使用 is
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] 的访问变得类型安全。
高级技巧:自定义类型谓词
你可以为任意复杂条件创建类型谓词:
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 工具集是通往静态类型安全世界的桥梁。