为何使用Number.isInteger校验后,TypeScript仍警告对象可能为null/undefined?
先看你编写的代码:
if ( Number.isInteger(prevObj?.prop) && Number.isInteger(nextObj?.prop) && prevObj.prop !== nextObj.prop ) { ... }
出现警告的核心原因是TypeScript对两种校验逻辑的类型收窄能力支持不同:
typeof 是TS原生支持的类型守卫
当使用typeof prevObj?.prop === 'number'时,TS能完全理解这个表达式的逻辑:如果prevObj是null或undefined,prevObj?.prop会返回undefined,此时typeof的结果是'undefined',不满足校验条件;只有当prevObj非空且prop是number类型时,表达式才会返回true。因此TS会自动将prevObj的类型收窄,排除null/undefined的可能,不会出现警告。Number.isInteger 默认不被TS识别为类型守卫
虽然从JavaScript逻辑上,Number.isInteger只有在传入值是整数(number类型的子集)时才返回true,但TypeScript的默认类型定义中,Number.isInteger的参数是unknown,返回值仅标记为boolean,没有用类型谓词(value is number)明确告知TS:返回true时输入值一定是number类型。这就导致TS无法通过
Number.isInteger(prevObj?.prop)返回true这个结果,倒推出prevObj?.prop是number类型,更没法推断出prevObj本身不是null/undefined——毕竟prevObj?.prop如果是undefined,Number.isInteger会返回false,但TS没法反向推导这个逻辑,所以依然认为prevObj可能为null/undefined,从而抛出警告。
如果想让Number.isInteger也具备类型收窄能力,可以手动扩展它的类型定义:
declare global { interface NumberConstructor { isInteger(value: unknown): value is number; } }
添加这个定义后,TS就能识别Number.isInteger的类型守卫特性,自动收窄输入类型,警告也会消失。
内容的提问来源于stack exchange,提问作者Stafford Rose

