为何联合数组类型可调用array.some()却无法调用array.every()?
array.some()能正常调用,array.every()却触发类型错误? 先看我们的基础函数:
function doSomething(input: number|string): boolean { if (input === 42 || input === '42') { return true; } else { return false; } }
下面这段调用array.some()的代码可以正常通过类型检查:
function doSomethingWithArray(input: number[]|string[]): boolean { return input.some(i => doSomething(i)); }
但调用array.every()的代码却会报错:
function doEverythingWithArray(input: number[]|string[]): boolean { return input.every(i => doSomething(i)); }
错误信息如下:
This expression is not callable.
Each member of the union type '{ (predicate: (value: number, index: number, array: number[]) => value is S, thisArg?: any): this is S[]; (predicate: (value: number, index: number, array: number[]) => unknown, thisArg?: any): boolean; } | { ...; }' has signatures, but none of those signatures are compatible with each other.
问题核心:some与every的类型签名差异
这俩方法的区别在于重载逻辑的返回值处理:
1. Array.some()的签名兼容
some的所有重载最终返回的都是boolean——不管你传入的是普通断言函数,还是带类型谓词的断言函数,对外暴露的返回类型都是布尔值。
对于number[]|string[]这个联合类型来说,number[]的some和string[]的some方法签名完全兼容:它们都接受能处理对应元素的回调,且返回值统一为boolean。你的回调i => doSomething(i)既能接收number也能接收string,完美匹配两个分支的要求,所以类型检查通过。
2. Array.every()的签名冲突
every有一个特殊的重载:当你传入的回调是类型谓词函数(比如(val: T) => val is S)时,every会返回一个类型守卫this is S[],也就是会缩小整个数组的类型。
这就导致了问题:
number[]的every有一个重载返回this is S[](S是number的子类型)string[]的every有一个重载返回this is S[](S是string的子类型)
这两个重载的返回类型无法被TypeScript统一,它无法确定你调用的是哪个分支的重载,因此抛出“签名不兼容”的错误。
解决方法
如果要让doEverythingWithArray通过类型检查,有两种常用方式:
方式一:明确指定回调参数类型
让TypeScript忽略类型谓词的重载分支,强制使用返回boolean的普通重载:
function doEverythingWithArray(input: number[]|string[]): boolean { return input.every((i: number|string) => doSomething(i)); }
方式二:类型断言(谨慎使用)
把联合类型数组断言为Array<number|string>,跳过联合类型的签名检查:
function doEverythingWithArray(input: number[]|string[]): boolean { return (input as Array<number|string>).every(i => doSomething(i)); }
内容的提问来源于stack exchange,提问作者Guaycuru

