如何在 TypeScript 中将数组转换为对象?

3
我试图输入下面的实用函数,该函数接受一个数组并返回以下对象。

const convertListToMap = (list, key, secondKey) => {
  const map = {};
  
  for (const ele of list) {
    map[ele[key]] = ele[secondKey]
  }
  
  return map;
}


console.log(convertListToMap([{name: 'isaac', age: 17}, {name: 'john', age: 20}], 'name', 'age'));

console.log(convertListToMap([{race: 'chinese', language: 'mandarin'}, {race: 'malay', language: 'melayu'}], 'race', 'language'));

如您所见,keysecondKey 是有点动态的,取决于参数 list,因此我想使用 generics。以下是我的尝试:

const convertListToMap = <
  T extends object,
  U extends keyof T,
  V extends keyof T
>(
  list: T[],
  key: U,
  secondKey: V
) => {
  const map = {} as Record<U, V>;
  for (const ele of list) {
    map[ele[key]] = ele[secondKey];
  }
  return map;
};

然而,上述代码报错提示 Type 'T[U]' cannot be used to index type 'Record<U, V>',不知道哪里出错了?

更新

我最新的尝试是按照以下方式更新map方法的签名:

  const map = {} as Record<T[U], T[V]>;
  for (const ele of list) {
    map[ele[key]] = ele[secondKey];
  }
  return map;

在这最新的尝试中,对map变量进行值分配很好,但我在下面记录声明行处遇到错误:
类型'T[U]'不能满足约束条件'string | number | symbol'。 类型“T[keyof T]”不能赋值给类型“string | number | symbol”。 类型“T[string] | T[number] | T[symbol]”不能赋值给类型“string | number | symbol”。 类型“T[string]”不能赋值给类型“string | number | symbol”。 类型“T[string]”不能赋值给类型“symbol”。 类型“T[keyof T]”不能赋值给类型“symbol”。 类型“T[U]”不能赋值给类型“symbol”。 类型“T[keyof T]”不能赋值给类型“symbol”。 类型“T[string] | T[number] | T[symbol]”不能赋值给类型“symbol”。 类型“T[string]”不能赋值给类型“symbol”。ts(2344)

忽略我(删除了评论)。我认为主要问题在于你的map不是类型Record<U,V>,因为你放入的值来自T,而不是键。 - Phil
@Phil:说得好,我甚至尝试了 const map = {} as Record<T[U], T[V]>; 但它也不起作用哈哈。 - Isaac
@Phil:我终于找到了一个解决方案,如果你感兴趣的话可以看一下 :D - Isaac
1个回答

2
上次尝试的问题在于Record似乎只期望string | number | symbol,而由于TypeScript不确定T[U]将是什么,因此它会抛出错误。我发现我们可以通过以下方式告诉TS更多关于T的信息。
const convertListToMap = <
  T extends { [key: string|number]: string | number },
  U extends keyof T,
  V extends keyof T
>(
  list: T[],
  key: U,
  secondKey: V
) => {
  const map = {} as Record<T[U], T[V]>;
  for (const ele of list) {
    map[ele[key]] = ele[secondKey];
  }
  return map;
};

从上面可以看出,我们告诉typescript T 将是一个具有string键的对象,值将是类型为string | number的值。通过这样做,TypeScript对定义Record<T[U], T[V]>感到满意。
这个解决方案非常好,TS可以正确推断。
const personObj = convertListToMap([{name: 'isaac', age: 17}, {name: 'john', age: 20}], 'name', 'age')
// const personObj: Record<string, number>

const languageObj = convertListToMap([{race: 'chinese', language: 'mandarin'}, {race: 'malay', language: 'melayu'}], 'race', 'language')
// const languageObj: Record<string,string>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接