I am trying to typed the below utility function which takes in an array and return an object as below
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'));
I am trying to typed the below utility function which takes in an array and return an object as below
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'));
As you can tell key
and secondKey
is sort of dynamic, depending on the parameter list
, so I guess generics
is the way to go. Below is my attempt
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;
};
However, the above giving me error Type 'T[U]' cannot be used to index type 'Record<U, V>'
, wondering which part I've done wrong?
UPDATES
My latest attempt is to update the signature of map
as below
const map = {} as Record<T[U], T[V]>;
for (const ele of list) {
map[ele[key]] = ele[secondKey];
}
return map;
With this latest attempt, value assignment to map
variable is fine, but I'm getting error at Record declaration line with below error
Share Improve this question edited Sep 28, 2021 at 12:53 Isaac asked Sep 28, 2021 at 7:37 IsaacIsaac 12.9k18 gold badges64 silver badges134 bronze badges 3Type 'T[U]' does not satisfy the constraint 'string | number | symbol'. Type 'T[keyof T]' is not assignable to type 'string | number | symbol'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'string | number | symbol'. Type 'T[string]' is not assignable to type 'string | number | symbol'. Type 'T[string]' is not assignable to type 'symbol'. Type 'T[keyof T]' is not assignable to type 'symbol'. Type 'T[U]' is not assignable to type 'symbol'. Type 'T[keyof T]' is not assignable to type 'symbol'. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'symbol'. Type 'T[string]' is not assignable to type 'symbol'.ts(2344)
-
Ignore me (deleted the ment). I think the main problem is that your
map
is not typeRecord<U, V>
since you're putting in the values fromT
, not the keys – Phil Commented Sep 28, 2021 at 7:49 -
@Phil: Good point, I even tried
const map = {} as Record<T[U], T[V]>;
but it doesn't work either haha – Isaac Commented Sep 28, 2021 at 7:50 - @Phil: I've finally found a solution, maybe can take a look if you're interested :D – Isaac Commented Sep 28, 2021 at 8:22
1 Answer
Reset to default 4The problem with the last attempt was that Record
seemed to expect only string | number | symbol
, and since TypeScript unsure what T[U]
gonna be, hence it threw the error. I found that we can tell TS a little bit more about T
as below
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;
};
From the above, we are telling typescript that T
is gonna be an object with key
of string
, and the value is gonna be of type string | number
, with that, TypeScript is happy with the definition Record<T[U], T[V]>
This solution works great and TS can infer correctly
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>