A friend of mine was trying to write a type which did a conversion similar to the runtime behavior of Object.fromEntries; that is convert a type for an array of two-element tuples (say [["a", number], ["b", string]]) to a type for an object with those types as key-value pairs (in this case, {a: number; b: string}).
His first attempt to do this was the below, which didn't work:
type ObjectKey = string | number;
type EntryKey<T> = T extends [infer K, unknown]
? K extends ObjectKey
? K
: never
: never;
type EntryValue<T> = T extends [ObjectKey, infer V] ? V : never;
type ObjFromEntriesBad<Entries extends [ObjectKey, unknown][]> = {
[Index in keyof Entries as EntryKey<Entries[Index]>]: EntryValue<
Entries[Index]
>;
};
// Incorrect: is `{a: string | number; b: string | number;}
type Test1 = ObjFromEntriesBad<[["a", number], ["b", string]]>
I wrote a simplified version which I thought should work, but it failed with compiler errors:
// Error: Type '0' cannot be used to index type 'Entries[Index]'
type ObjFromEntriesBad2<Entries extends [ObjectKey, unknown][]> = {
[Index in keyof Entries as Entries[Index][0]]: Entries[Index][1]
};
I finally came up with a solution that seemed to work:
type ObjFromEntriesGood<Entries extends [ObjectKey, unknown][]> = {
[Tup in Entries[number] as Tup[0]]: Tup[1]
};
My questions are then: why didn't the first solution work? Why does the second solution cause compiler errors? And finally, in the working solution we iterate of Entries[number], but is there a way to create a correct type while still iterating over keyof Entries in the mapped type?