Búsqueda de sitios web

javascript - ¿Cómo convertir un arreglo en un objeto en TypeScript?


Estoy intentando escribir la siguiente función de utilidad que recibe un arreglo y devuelve un objeto como el siguiente

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'));

Como puedes ver, key y secondKey son algo dinámicos, dependiendo del parámetro list, así que supongo que generics es el camino a seguir. A continuación está mi intento

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;
};

Sin embargo, lo anterior me da el error Type 'T[U]' cannot be used to index type 'Record<U, V>', ¿me pregunto qué parte he hecho mal?

ACTUALIZACIONES

Mi último intento es actualizar la firma de map como se indica a continuación

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

Con este último intento, la asignación de valor a la variable map está bien, pero estoy obteniendo un error en la línea de declaración de Record con el siguiente error

El tipo 'T[U]' no cumple con la restricción 'string | number | symbol'. El tipo 'T[keyof T]' no se puede asignar al tipo 'string | number | symbol'. El tipo 'T[string] | T[number] | T[symbol]' no se puede asignar al tipo 'string | number | symbol'. El tipo 'T[string]' no se puede asignar al tipo 'string | number | symbol'. El tipo 'T[string]' no se puede asignar al tipo 'symbol'. El tipo 'T[keyof T]' no se puede asignar al tipo 'symbol'. El tipo 'T[U]' no se puede asignar al tipo 'symbol'. El tipo 'T[keyof T]' no se puede asignar al tipo 'symbol'. El tipo 'T[string] | T[number] | T[symbol]' no se puede asignar al tipo 'symbol'. El tipo 'T[string]' no se puede asignar al tipo 'symbol'.ts(2344)

Respuesta #1

El problema con el último intento fue que Record parecía esperar solo string | number | symbol, y como TypeScript no estaba seguro de qué sería T[U], por eso lanzó el error. Encontré que podemos decirle a TS un poco más sobre T como se muestra a continuación

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;
};

A partir de lo anterior, le estamos diciendo a TypeScript que T va a ser un objeto con key de string, y el valor va a ser del tipo string | number, con eso, TypeScript está satisfecho con la definición Record<T[U], T[V]>

Esta solución funciona muy bien y TS puede inferir correctamente

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>

Artículos relacionados