带数组的TypeScript相交类型归约

mar*_*ter 1 reduce types typescript

我正在尝试使类型与基本合并两个对象的通用数组reduce函数一起使用。以下代码段是实际代码的转储版本。为什么是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 : {}
Run Code Online (Sandbox Code Playgroud)

Ben*_*son 6

的签名reduce

reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U
Run Code Online (Sandbox Code Playgroud)

T是数组本身的类型参数。)因此,面对代码

[fooRes, barRes].reduce(flatten, {})
Run Code Online (Sandbox Code Playgroud)

类型检查器的工作是弄清楚是什么U。让我们看一下其推理:

  1. fooRes : IFoobarRes : IBar,所以[fooRes, barRes] : (IFoo | IBar)[]
  2. 因此,数组的 T ~ IFoo | IBar
  3. 因此flatten,其T参数设置为IFoo | IBar
  4. flattenK & T因此,的返回类型()为K & (IFoo | IBar)
  5. 由于flatten的返回类型必须可分配给U,这给我们带来了约束U >= (U & (IFoo | IBar)),它简化为U >= (IFoo | IBar)
  6. 另一个证据是initialValue参数的类型为{}
  7. 所以 U >= {}
  8. 这两个约束的最小上限为{}。因此,类型检查器进行推断U ~ {}

为什么没有意识到返回类型是IFoo & IBar?类型检查器无需考虑代码的运行时行为- flatten在整个简化过程中,的参数采用各种不同的类型。类型数组(IFoo | IBar)[]不能保证同时包含IFoos和IBars-可能只是IFoos 的数组。推断flatten出异构列表会压缩其组成类型将需要相当复杂的证明,并且期望机器能够为您编写这样的证明似乎并不合理。