我正在尝试让类型与通用数组归约函数一起使用,该函数基本上合并两个对象。以下代码片段是真实代码的转储版本。为什么
fl
的类型是{}
而不是IFoo & IBar
?
(我知道这个特定的示例可以轻松地用单个
Object.assign()
调用替换。)
const flatten = <K, T>(prev: K, x: T): K & T => {
return Object.assign(prev, x);
};
interface IFoo {
foo: true;
}
interface IBar {
bar: true;
}
const fooRes: IFoo = { foo: true };
const barRes: IBar = { bar: true };
const fl = [fooRes, barRes].reduce(flatten, {});
console.log(fl); // here, fl : {}
reduce
的签名是
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U
(
T
是数组本身的类型参数。)所以,面对代码
[fooRes, barRes].reduce(flatten, {})
类型检查器的工作是找出
U
是什么。让我们来看看它的推理:
fooRes : IFoo
和 barRes : IBar
,所以 [fooRes, barRes] : (IFoo | IBar)[]
T ~ IFoo | IBar
flatten
正在被调用,其 T
参数设置为 IFoo | IBar
flatten
的返回类型 (K & T
) 是 K & (IFoo | IBar)
flatten
的返回类型必须可分配给 U
,这给了我们约束 U >= (U & (IFoo | IBar))
,它简化为 U >= (IFoo | IBar)
initialValue
参数,其类型为 {}
U >= {}
{}
。所以类型检查器推断出 U ~ {}
。为什么它没有意识到返回类型是
IFoo & IBar
?类型检查器不会推理代码的运行时行为 - flatten
的参数在整个归约过程中采用各种不同的类型。 (IFoo | IBar)[]
类型的数组不能保证同时包含 IFoo
和 IBar
- 它可能只是一个 IFoo
数组。推断出异构列表会压缩其构成类型需要相当复杂的证明,并且期望机器能够为您编写这样的证明似乎并不合理。