繁体   English   中英

如何正确键入将对象数组转换为对象对象的函数

[英]How to properly type function that convert array of objects into object of objects

我正在使用.reduce尝试这种语法:

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R {
  return list.reduce((result: R, item: T): R => {
    result[item.key] = item;
    return result;
  }, {});
}

但是 Flow 给出了以下错误:

call of method `reduce`. Function cannot be called on any member of intersection type

根本问题在于协调泛型类型R{} ,即累加器的初始值。

一个 hacky 修复会阻止 Flow 尝试协调类型:

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R {
  var accum: any = {};
  return list.reduce((result: R, item: T): R => {
    result[item.key] = item;
    return result;
  }, accum);
}

但是这存在一些问题,我们将在下面进一步了解。 当我们明确说明累加器是什么类型时,事情变得更清楚了:

function arrToObject<T: {key: string}, R: {[string]: T}>(list: Array<T>): R {
  let accum: R = {};
  return list.reduce((result: R, item: T): R => {
    result[item.key] = item;
    return result;
  }, accum);
}

这迫使有问题的类型协调成为一个更简单的语句,周围没有太多噪音。 它会产生更好的错误:

3:   let accum: R = {};
                    ^ object literal. This type is incompatible with
3:   let accum: R = {};
                ^ some incompatible instantiation of `R`

这个错误并不能说明全部情况,但它使我们更接近真实情况。

Flow 要求泛型参数R可以是与约束兼容的任何类型。 因此, R有效实例可能比您对其施加的约束更通用,例如具有额外的必填字段。

这就产生了一个问题。 在函数体内部,您不可能知道R的实际实例化是什么样的。 所以你不能构造一个。 尽管错误消息很糟糕,但 Flow 是正确的,可以阻止您这样做! 如果有人使用R实例化为具有额外字段的内容调用您的函数,例如{[string]: {key: string}, selected: boolean}怎么办? 然后,您必须以某种方式知道用selected: boolean字段初始化累加器。

如果可以,比上述 hack 更好的解决方案是完全删除泛型:

type KeyMap = {[string]: {key: string}};
type Item = {key: string};

function arrToObject(list: Array<Item>): KeyMap {
  return list.reduce((result:KeyMap, item: Item): KeyMap => {
    result[item.key] = item;
    return result;
  }, {});
}

我还引入了一些类型别名来防止函数签名失控。

现在泛型被具体类型所取代,事情变得简单多了。 函数体可以为累加器创建一个新的初始值,因为它的确切类型是已知的。

如果出于某种原因确实需要泛型类型参数,也可以通过将累加器的初始值作为另一个参数传递给arrToObjectarrToObject 这将起作用,因为您的arrToObject的调用者将知道具体类型并能够实例化它。

@PeterHall 的回答正确地确定了类型检查失败的原因,但解决方案不是最佳的。 正如答案所说,问题在于R可能是满足{[string]: T}任何类型,但实际上所需的正是该类型 相反,需要一个泛型类型参数保留项目类型时项目进入和流出的arrToObject功能,具有{key: string}是的输入时所需的最低签名。

此签名有效:

function arrToObject<T: {key: string}>(list: Array<T>): {[string]: T}

或者,使用命名类型:

type KeyMap<T> = {[string]: T};
type Item = {key: string};

function arrToObject<T: Item>(list: Array<T>): KeyMap<T>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM