在我的项目开发中,咱们时常会遇到判断某个变量是否为一个有效值,或者依据变量的类型,依据不同的类型进行不同的操作的状况。
比方最常见的,判断一个变量是否为 Truthy
值(什么是 Truthy 值):
if (value !== null && value !== undefined) {// 搞事件}
咋看之下,也就短短两个语句,但这个事件须要进行 10 次、100 次的时候,或者你会开始想到封装:
function isDefined(value) {return value !== undefined && value !== null}
既然有了这个想法,何不一干到底,咱们就间接来封装一个本人的 is
办法库。
下列的办法,将应用规范的
TypeScript
编写,你将会看到:泛型、类型谓词is
。
一些惯例的 is
办法
通过类型谓词 is
能够在 TypeScript
收窄类型,帮忙更好的类型推断,这里不开展。
判断 Truthy
和 Falsy
:
// 能够思考一下 value !== undefined 和 typeof value !== 'undefined' 有什么区别?// null 呢?function isDefined<T = unknown>(value: T | undefined | null): value is T {return value !== undefined && value !== null}
function isNull(value: unknown): value is null | undefined {return value === undefined || value === null}
判断其余根本类型(除了 null
和 undefined
):
function isNumber(value: unknown): value is number {return typeof value === 'number'}
// 发问:NaN 是不是一个根本类型呢?function isNaN(value: unknown): value is number {return Number.isNaN(value)
}
function isString(value: unknown): value is string {return typeof value === 'string'}
function isBoolean(value: unknown): value is boolean {return typeof value === 'boolean'}
// 严格判断 true
function isTrue(value: unknown): value is true {return value === true}
// 严格判断 false
function isFalse(value: unknown): value is false {return value === false}
// 别忘了 Symbol
function isSymbol(value: unknown): value is symbol {return typeof value === 'symbol'}
// 还有一个根本类型,它是谁呢?
除开根本类型后,接下来就是一些常见对象类型的判断的,在这个之前能够先思考一个问题:
typeof object === 'object'
能不能无效的判断一个变量是否为对象呢?
从狭义上来讲,只有这个成立,那该变量的确是一个对象,但这往往不是咱们所须要和冀望的,因为这样并不能辨别数组 []
和 对象 {}
的区别,包含一些其余对象如 Date
。
所以咱们要借助一个 大家都晓得的 绕一点的形式来判断:Object.prototype.toString
,这里咱们就间接上了,不晓得具体原理的请自行搜寻。
常见对象的判断:
// 存一下,缩小对象属性的读取
const toString = Object.prototype.toString
function is(value: unknown, type: string) {return toString.call(value) === `[object ${type}]`
}
// 这里能够思考对象类型的收窄,用 Record<string, any> 是否适合?function isObject<T extends Record<string, any> = Record<string, any>>(value: unknown): value is T {return is(value, 'Object')
}
// 数组能够应用原生的办法取得更高的效率
function isArray(value: unknown): value is any[] {return Array.isArray(value)
}
// 插播一个 function
function isFunction(value: unknown): value is (...any: any[]) => any {return typeof value === 'function'}
// 补充下面被忘记的 BigInt 根本类型
function isBigInt(value: unknown): value is bigint {return typeof value === 'bigint'}
// 这里如果想要同时反对 PromiseLike 的类型收窄的话要怎么写呢?function isPromise(value: unknown): value is Promise<any> {
return (
!!value &&
typeof (value as any).then === 'function' &&
typeof (value as any).catch === 'function'
)
}
function isSet(value: unknown): value is Set<any> {return is(value, 'Set')
}
function isMap(value: unknown): value is Map<any, any> {return is(value, 'Map')
}
function isDate(value: unknown): value is Date {return is(value, 'Date')
}
function isRegExp(value: unknown): value is RegExp {return is(value, 'RegExp')
}
留神到这里独自封装了一个 is
办法,这个办法是能够进行任意的拓展的,比方想判断一些自定义类的时候,能够基于该 is
再封装(下面的办法都是这个准则):
function isMyClass(value: unknown): value is MyClass {return is(value, 'MyClass')
}
一些不太惯例的 is
办法
除了一些类型的判断,咱们时常会有呈现像是判断该变量是否为一个 Empty
值:
什么是
Empty
指的,惯例一点来讲就是包含:空数组、空字符串、空Map
、空Set
、空对象{}
。
function isEmpty(value: unknown) {if (Array.isArray(value) || typeof value === 'string') {return value.length === 0}
if (value instanceof Map || value instanceof Set) {return value.size === 0}
if (isObject(value)) {return Object.keys(value).length === 0
}
return false
}
还有一个比拟常见的场景是,判断某个变量是否是否个对象的键值,咱们能够借助 Object.prototype.hasOwnProperty
来判断:
const hasOwnProperty = Object.prototype.hasOwnProperty
function has(value: Record<string, any>, key: string | symbol): key is keyof typeof value {return hasOwnProperty.call(value, key)
}
整合一下
好了,到此为止一个蕴含了根本类型和一些常见类型的 is
函数库就功败垂成了,最初附上一份整合后的残缺代码,大家能够在这个根底上做一些本人的拓展(应该没人须要纯 ):js
版本的吧
const toString = Object.prototype.toString
const hasOwnProperty = Object.prototype.hasOwnProperty
export function is(value: unknown, type: string) {return toString.call(value) === `[object ${type}]`
}
export function has(value: Record<string, any>, key: string | symbol): key is keyof typeof value {return hasOwnProperty.call(value, key)
}
export function isDefined<T = unknown>(value: T | undefined | null): value is T {return value !== undefined && value !== null}
export function isNull(value: unknown): value is null | undefined {return value === undefined || value === null}
export function isNumber(value: unknown): value is number {return typeof value === 'number'}
export function isNaN(value: unknown): value is number {return Number.isNaN(value)
}
export function isString(value: unknown): value is string {return typeof value === 'string'}
export function isBoolean(value: unknown): value is boolean {return typeof value === 'boolean'}
export function isTrue(value: unknown): value is true {return value === true}
export function isFalse(value: unknown): value is false {return value === false}
export function isSymbol(value: unknown): value is symbol {return typeof value === 'symbol'}
export function isBigInt(value: unknown): value is bigint {return typeof value === 'bigint'}
export function isArray(value: unknown): value is any[] {return Array.isArray(value)
}
export function isObject<T extends Record<string, any> = Record<string, any>>(value: unknown): value is T {return is(value, 'Object')
}
export function isPromise(value: unknown): value is Promise<any> {
return (
!!value &&
typeof (value as any).then === 'function' &&
typeof (value as any).catch === 'function'
)
}
export function isFunction(value: unknown): value is (...any: any[]) => any {return typeof value === 'function'}
export function isSet(value: unknown): value is Set<any> {return is(value, 'Set')
}
export function isMap(value: unknown): value is Map<any, any> {return is(value, 'Map')
}
export function isDate(value: unknown): value is Date {return is(value, 'Date')
}
export function isRegExp(value: unknown): value is RegExp {return is(value, 'RegExp')
}
export function isEmpty(value: unknown) {if (Array.isArray(value) || typeof value === 'string') {return value.length === 0}
if (value instanceof Map || value instanceof Set) {return value.size === 0}
if (isObject(value)) {return Object.keys(value).length === 0
}
return false
}
一些碎碎念
最近回忆了这几年的工作,发现自己封装过各种各样的工具函数,但很多都是零零散散地遍布在我的项目中。
也是出于整顿和温习的目标,想着分享一下本人写过的一些货色,于是便尝试写了这篇文章,心愿能帮忙到一些人。