在项目开发中,我们经常会遇到判断一个变量是否为有效值,或者根据变量的类型进行不同操作的情况。比如最常见的判断一个变量是否为Truthy值(什么是Truthy值):if(value!==null&&value!==undefined){//dothings}乍一看有只是两个简短的语句,但是当这个东西需要做10次或者100次的时候,也许你会开始想到封装:functionisDefined(value){returnvalue!==undefined&&value!==null}既然你有这个想法,为什么不走到最后,我们就直接封装自己的is方法库。以下是用标准TypeScript编写的方法,您将看到:泛型、类型谓词。一些常规的is方法可以通过类型谓词来缩小TypeScript中的类型,以帮助更好地进行类型推断,这里不再展开。JudgeTruthyandFalsy://你可以想想value!==undefined和typeofvalue!=='undefined'的区别?//空值呢?functionisDefined(value:T|undefined|null):值为T{returnvalue!==undefined&&value!==null}functionisNull(value:unknown):值为null|undefined{返回值===undefined||value===null}判断其他基本类型(除了null和undefined):functionisNumber(value:unknown):valueisnumber{returntypeofvalue==='number'}//问题:NaN是不是基本类型?functionisNaN(value:unknown):valueisnumber{returnNumber.isNaN(value)}functionisString(value:unknown):valueisstring{returntypeofvalue==='string'}函数isBoolean(value:unknown):valueisboolean{returntypeofvalue==='boolean'}//严格判断为真valueisfalse{returnvalue===false}//别忘了SymbolfunctionisSymbol(value:unknown):valueisssymbol{returntypeofvalue==='symbol'}//还有一个基本类型,是谁呢?排除了基本类型之后,接下来就是判断一些常见的对象类型。在此之前,可以思考一个问题:typeofobject==='object'能否有效判断一个变量是否为对象?从广义上讲,只要是这样,变量确实是一个对象,但这往往不是我们所需要和期望的,因为它没有区分数组[]和对象{}的区别,包括一些其他的对象比如Dateso需要用大家熟知的方式来判断:Object.prototype.toString,这里直接上,具体原理不清楚的请自行搜索。普通对象的判断://保存下来减少对对象属性的读取consttoString=Object.prototype.toStringfunctionis(value:unknown,type:string){returntoString.call(value)===`[object${type}]`}//这里可以考虑对象类型的缩小,用Record合适吗?functionisObject=Record>(value:unknown):valueisT{returnis(value,'Object')}//数组可以使用native方法来获得更高的效率functionisArray(value:unknown):valueisany[]{returnArray.isArray(value)}//插入一个函数functionisFunction(value:unknown):valueis(...any:any[])=>any{returntypeofvalue==='function'}//补充上面遗忘的BigInt基本类型functionisBigInt(value:unknown):valueisbigint{returntypeofvalue==='bigint'}//这里如果你要如何如果同时缩小支持PromiseLike的类型,要写吗?functionisPromise(value:unknown):valueisPromise{return(!!value&&typeof(valueasany).then==='function'&&typeof(valueasany).catch==='函数')}函数isSet(值:unknown):值为Set{returnis(value,'Set')}functionisMap(value:unknown):valueisMap{returnis(value,'Map')}functionisDate(value:unknown):valueisDate{returnis(value,'Date')}functionisRegExp(value:unknown):valueisRegExp{returnis(value,'RegExp')}注意这里是单独封装的方法,这个方法可以任意扩展。例如,当你要判断一些自定义类时,你可以根据is重新打包(上面的方法都是基于这个原理):functionisMyClass(value:unknown):valueisMyClass{returnis(value,'MyClass')}一些非常规的is方法除了一些类型的判断,我们还经常会遇到判断变量是否为Empty值的情况:什么是Empty?一般来说就是包括:空数组、空字符串、空Map、空Set、空对象{}functionisEmpty(value:unknown){if(Array.isArray(value)||typeofvalue==='string'){returnvalue.length===0}if(valueinstanceofMap||valueinstanceofSet){返回value.size===0}if(isObject(value)){returnObject.keys(value).length===0}returnfalse}另一个常见的场景是判断一个变量是否是一个对象我们可以使用Object.prototype.hasOwnProperty判断键值:consthasOwnProperty=Object.prototype.hasOwnPropertyfunctionhas(value:Record,key:string|symbol):keyiskeyoftypeofvalue{returnhasOwnProperty.call(value,key)}来整合它。至此,一个包含基本类型和一些常用类型的is函数库就完成了。最后附上完整的集成代码。你可以在此基础上做一些自己的扩展(没有人应该需要纯js版本):consttoString=Object.prototype.toStringconsthasOwnProperty=Object.prototype.hasOwnPropertyexportfunctionis(value:unknown,type:string){returntoString.call(value)===`[object${type}]`}导出函数has(value:Record,key:string|symbol):keyiskeyoftypeofvalue{returnhasOwnProperty.call(value,key)}exportfunctionisDefined(value:T|undefined|null):valueisT{returnvalue!==undefined&&value!==null}导出函数isNull(value:未知):值为空|undefined{返回值===undefined||value===null}导出函数isNumber(value:unknown):valueisnumber{returntypeofvalue==='number'}exportfunctionisNaN(value:unknown):valueisnumber{returnNumber.isNaN(value)}导出函数isString(value:unknown):value是string{returntypeofvalue==='string'}exportfunctionisBoolean(value:unknown):value是boolean{returntypeofvalue==='boolean'}exportfunctionisTrue(值:未知):值为真{返回值===真}导出函数isFalse(值:未知):值为假{返回值===假}导出函数isSymbol(值:未知):值为符号{返回typeofvalue==='symbol'}导出函数isBigInt(value:unknown):value是bigint{returntypeofvalue==='bigint'}exportfunctionisArray(value:unknown):valueisany[]{returnArray.isArray(value)}exportfunctionisObject=Record>(value:unknown):valueisT{returnis(value,'Object')}exportfunctionisPromise(value:unknown):valueisPromise{返回(!!value&&typeof(任何值).then==='function'&&typeof(任何值).catch==='function')}exportfunctionisFunction(value:unknown):valueis(...any:any[])=>任何{returntypeofvalue==='function'}exportfunctionisSet(value:unknown):valueisSet{returnis(value,'Set')}exportfunctionisMap(value:unknown):值为Map{returnis(value,'Map')}exportfunctionisDate(value:unknown):valueisDate{returnis(value,'Date')}exportfunctionisRegExp(value:unknown):值是RegExp{返回是(值e,'RegExp')}exportfunctionisEmpty(value:unknown){if(Array.isArray(value)||typeofvalue==='string'){returnvalue.length===0}if(valueinstanceofMap复制代码||valueinstanceofSet){returnvalue.size===0}if(isObject(value)){returnObject.keys(value).length===0}returnfalse}一些随意的想法经过多年的工作,我发现我封装了各种工具功能,但是很多都散落在整个项目中,方便整理和回顾。我想分享一些我写的东西,所以我试着写了这篇文章,希望能帮助到一些人。更新:【封装小贴士】列表处理函数的封装【封装小贴士】数字处理函数的封装最后推荐一下我个人的开源项目VexipUI——GitHub上比较完整的Vue3组件库,支持全面的css变量和内置深色主题,fullTypeScript和combinedApi,其特点是几乎所有组件的每一个属性都支持通过配置修改其默认值(传递一个对象),这应该是其他组件库目前没有的特性吧~现在招募小伙伴使用或参与维护开发本项目,本人实力有限,文档、单元测试、服务端渲染支持、外设插件、用例等,只要你是有兴趣的可以从各个入口参与,非常欢迎~本期【封装小贴士】的内容源码在包含在@vexip-ui/utils包下,GitHub,这个包也是单独发布的,不过暂时还没有Api文档,可能需要直接查阅源码才能吃~