0

我发现自己经常使用像这样的数组

[
{key1: val1, key2: value2, key3: val3},
{key1: val1, key2: value2, key3: val3},
{key1: val1, key2: value2, key3: val3}]

我想把它转换成字典/地图,例如

[val1FromFirstElement]: {key2: val2, key3, val3},
[val1FromSecondElement]: {key2: val2, key3, val3},
[val1ThirdElement]: {key2: val2, key3, val3}}

我经常使用reduce,但这会使代码忙碌。我想写一个通用的助手,我们可以确保我们映射的数组期望我们想要索引的键(在上面的例子中,key1)。

function toDictionary<T, Key extends keyof T> (array: T[], key: Key) {
  return array.reduce((prev, cur: T) => {
      return{...prev, ...{[cur[key]]: cur}};
  }, {})
}

问题是[cur[key]]不会编译说明A computed property name must be of type 'string', 'number', 'symbol', or 'any'.

我相信索引访问类型只能是number | string | symbol,但我不明白为什么我必须指定其他任何内容,因为打字稿应该知道键类型只能是这些类型之一?正确的?

希望有人能告诉我我的误解在哪里。

4

1 回答 1

1

您得到的这个特定错误是因为TintoDictionary不受约束,这使得Key不确定。换句话说,就目前而言,您的函数将接受T 的任何类型。

例如,尝试将您的函数调用为toDictionary([3,4,5], ""). 你会看到第一个参数通过了。为什么?因为T[]可以是任何数组,因为T是不受约束的。第二个参数会产生一个错误,因为keyof数字数组中元素的唯一类型是[3,4,5]任何数字的原型属性,例如, toString,toFixed等。因此,实际上,toDictionary([3,4,5], "toString")即使不是什么,也可以很好地传递您的输入你要。

问题是你真正想要的T只是真正有键的类型,就像你给出的例子一样{key1: val1, key2: value2, key3: val3}。所以你需要以某种方式约束 T 。将其限制为可以解决extends Record<string,any>问题,因为现在T需要一些由字符串索引的对象(如您提供的示例中所示)。例如,这种类型适用于您给出的示例:

function toDictionary<T extends Record<string,any>, Key extends keyof T> (array: T[], key: Key) {
  return array.reduce((prev, cur: T) => {
      return{...prev, ...{[cur[key]]: cur}};
  }, {})
}

您会看到toDictionary([3,4,5], "toString")上面的人为示例现在正确失败。

您还可以在约束中更加明确,例如T extends {key1:any, key2:any, key3: any}限制为具有该形式的对象。这真的取决于你想如何使用它。

(FWIW,如果您传递的数组有多个具有相同值的项目,我不清楚您期望的行为是什么key1。您当前的函数只会用迭代中的最后一个值覆盖任何先前的值;那可能不是你想要什么。)

于 2022-02-12T13:03:55.623 回答