不能将索引访问类型与 T[Key extends keyof T] 一起使用

Cannot use index access type with T[Key extends keyof T]

我发现自己经常使用像

这样的数组
[
{key1: val1, key2: value2, key3: val3},
{key1: val1, key2: value2, key3: val3},
{key1: val1, key2: value2, key3: val3}]

我想将其转换为 dictionary/map,例如

[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,但我不明白为什么我必须指定其他任何内容,因为打字稿应该知道键类型只能是这些类型之一?对吗?

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

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

例如,尝试将函数调用为 toDictionary([3,4,5], "")。您将看到第一个参数通过正常。为什么?因为 T[] 可以是 任何 数组,因为 T 是不受约束的。第二个参数会产生错误,因为数字数组中元素的唯一 keyof 类型,如 [3,4,5] 是任何数字的原型属性,如 toStringtoFixed、等等。因此,实际上,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 的项目,您期望的行为是什么。您当前的函数将只覆盖任何以前的值迭代中的最后一个;这可能不是您想要的。)