You can use reducer pattern for both type and runtime scopes.
export interface SomeNumbers<N, V> {
name: N;
value: V;
}
type Reducer<
Tuple extends Array<any>,
Acc extends Record<string, any> = {},
Index extends number[] = []
> =
Tuple extends []
? Acc
: (Tuple extends [infer Head, ...infer Tail]
? (Head extends PropertyKey
? Reducer<
Tail,
Acc & Record<Head, SomeNumbers<Head, Index['length']>>,
[...Index, 1]
>
: never)
: never)
const builder = <Tuple extends string[]>(tuple: [...Tuple]) =>
tuple.reduce((acc, elem, index) => ({
...acc,
[elem]: {
name: elem,
value: index
}
}), {} as Reducer<Tuple>)
const result = builder(['first', 'second', 'third'])
// {
// name: "first";
// value: 0;
// }
result.first
Playground
Reducer type does exactly the same job as builder function. Except Reducer iterates recursively through the tuple.
Reducer - iterates recursively through the Tuple and intersects Acc with SomeNumbers<N,V>. Index - is extra tuple for indexing. Every time I increasing Index tuple for 1 element. It just coresponds to the index of iterated element
You can find more examples in my blog