TypeScript可选类型谓词否定分支收窄错误解决方法
TypeScript 类型谓词取反收窄错误的解决方案
问题本质
TypeScript 原生类型谓词的默认逻辑是:若谓词声明为input is T,则返回false的分支会自动将入参类型收窄为原联合类型排除 T 后的剩余类型。
最初实现的核心问题是给了 TypeScript 错误的类型承诺:声明返回true时入参是全量number类型,等价于告诉 TS 返回false时入参一定不属于number类型,但实际实现中返回false的场景包含NaN、Infinity这类本身就属于number类型的值,最终导致 else 分支类型推导错误。
正确实现方案
通过**带标记的精确子类型(Branded Type)**修正类型承诺,明确告诉 TS 函数判断的是「入参是否为有限实数」而非「是否为 number」,即可同时满足正反分支的正确收窄,不需要额外修改调用侧逻辑:
// 定义有限实数的子类型,作为number的精确子集 type FiniteNumber = number & { readonly __finite: unique symbol }; function isRealNumber(input: number | undefined | null): input is FiniteNumber { return input !== undefined && input !== null && Number.isFinite(input); }
效果验证
const myNumber: number | null = NaN as any; if (isRealNumber(myNumber)) { const b = myNumber; // 类型为FiniteNumber,属于number子类型,可直接当number使用,符合预期 } else { const b = myNumber; // 类型为number | null,推导完全正确 } const val2: number | undefined | null = Infinity; if (isRealNumber(val2)) { // val2 收窄为FiniteNumber } else { const c = val2; // 类型为number | undefined | null,符合预期 }
方案说明
FiniteNumber是number的子类型,所有需要传入number的场景都可以直接传入该类型的值,不会产生额外的类型兼容成本- 该方案只需要修正类型谓词的声明即可,完全符合单函数实现的要求
- 兼容所有支持类型谓词的 TypeScript 版本,无版本门槛
内容的提问来源于stack exchange,提问作者Daniel Centore
相关产品推荐
相关产品推荐

