In order to transform a tuple into the desired object type, we can introduce a generic type called ToOutputType
which utilizes a mapped type.
type ToOutputType<T extends { name: string, value: any }[]> = {
[K in T[number] as K["name"]]: { value: K["value"] }
}
Furthermore, we can enhance the functionality of arrayOfObjectsToObject
by making it generic.
type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never;
const arrayOfObjectsToObject =
<
T extends { name: K, value: any }[],
K extends string
>(array: readonly [...T]): Expand<ToOutputType<T>> => {
return array.reduce((acc, { name, value }) => {
acc[name] = { value };
return acc;
}, {} as any) as Expand<ToOutputType<T>>;
}
The function parameter T
represents the tuple input, while K
is utilized to narrow down strings within the tuple to literal types. The return type is specified as
Expand<ToOutputType<T>>
. The
Expand
type is mainly used for aesthetic purposes.
Upon calling the function, you will obtain the following result:
const result = arrayOfObjectsToObject([
{ name: 'test', value: 1 },
{ name: 'test2', value: 2 }
]);
const a = result.test
// ^? { value: number; }
const b = result.test2
// ^? { value: number; }
Note that the type of value
is inferred as number
in both instances. TypeScript automatically broadens the numbers to number
, but this behavior can be prevented by using as const
.
const result = arrayOfObjectsToObject([
{ name: 'test', value: 1 },
{ name: 'test2', value: 2 }
] as const);
const a = result.test
// ^? { value: 1; }
const b = result.test2
// ^? { value: 2; }
Playground
If avoiding the use of as const
is preferred, an additional special generic type for inference can be employed.
type Narrowable = string | number | boolean | symbol | object | undefined | void | null | {};
const arrayOfObjectsToObject =
<
T extends { name: K, value: N }[],
N extends { [k: string]: N | T | [] } | Narrowable,
K extends string
>(array: readonly [...T]): Expand<ToOutputType<T>> => {
return array.reduce((acc, { name, value }) => {
acc[name] = { value };
return acc;
}, {} as any) as Expand<ToOutputType<T>>;
}