带有数组 reduce 的 TypeScript 交集类型

TypeScript intersection types with array 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 : {}

The signature of reduce

reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U

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

[fooRes, barRes].reduce(flatten, {})

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

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

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