I'm close to nailing it, but can't find a way around the final TS2322: Type TcolTuple[i] is not assignable to type string | number | symbol compiler error.
So, here is a utility function rowsToObjects() that quite a few people probably defined in their projects once or twice, it's somewhat similar to zip() in concept:
const objects = rowsToObjects(
['id', 'color' , 'shape' , 'size' , 'to' ] as const,
[ 1n, 'red' , 'circle' , 'big' , '0x0'] as const,
[ 2n, 'green' , 'square' , 'small' , '0x0'] as const,
[ 3n, 'blue' , 'triangle', 'small' , '0x0'] as const,
)
That outputs:
[
{id: 1n, color: 'red', shape: 'circle', size: 'big', to: '0x0'},
{id: 2n, color: 'green', shape: 'square', size: 'small', to: '0x0'},
{id: 3n, color: 'blue', shape: 'triangle', size: 'small', to: '0x0'},
]
The actual implementation is obviously trivial, but typing it gives me some hard time:
export function rowsToObjects<
Tobj extends { [i in keyof TcolTuple as TcolTuple[i]]: TvalTuple[i] },
TcolTuple extends readonly string[],
TvalTuple extends { [j in keyof TcolTuple]: unknown }
>(cols: TcolTuple, ...rows: TvalTuple[]): Tobj[];
Current code seems logical to me, but the compiler complains about the as TcolTuple[i] part:
TS2322: Type TcolTuple[i] is not assignable to type string | number | symbol
Type TcolTuple[keyof TcolTuple] is not assignable to type string | number | symbol
Type
TcolTuple[string] | TcolTuple[number] | TcolTuple[symbol]
is not assignable to type string | number | symbol
Type TcolTuple[string] is not assignable to type string | number | symbol
Am I missing something obvious here? The typing is close to satisfactory, but without that as TcolTuple[i] it does not recognize which value belongs to which key and just unions them all.

I think the main problem you're having with
is that using key remapping prevents the mapped type from being homomorphic (see What does "homomorphic mapped type" mean?), so instead of mapping over just the numeric-like indices of the tuple like
"0" | "1" | "2", you're mapping over all the indices, includingnumber, a mixture of all the elements. And that gives you the union you're unhappy with.The easiest change here is to explicitly map over only the numeric-like indices, by intersecting
keyof TcolTuplewith the pattern template literal type`${number}`(as implemented in microsoft/TypeScript#40598. That removes anything that isn't a string version of a number. For example,"0" | "1" | "2" | number | "length" | "find"when intersected with`${number}`, gives you just"0" | "1" | "2".That more or less fixes it:
Personally, if I were writing this for myself, I would:
consttype parameters instead of requiring callers useconstassertions;rowsToObjects(["a"],[0],[1])should return[{a: 0}, {a: 1}]and not{a: 0 | 1}[];IandJinstead ofiandj, keeping to the naming convention to distinguish types from variables (in{[P in K]: F<P>}Pis a type parameter, not a variable name, sopcould be confusing).None of these are of vital importance, but it gives the output
Playground link to code