Reputation: 27
Given a type that is an array of objects, how do I create a new type that is an object, where the keys are values of a specific property in the array objects, and the values of the object are the type of one of the object generics.
With JS code, it would look like this:
const obj = arr.reduce((acc, element) => ({
...acc,
[element.name]: element.bar
}))
So far I have this:
type ArrayToObject<Arr extends Array<CustomType<any>> = []> = {
[K in keyof Arr]: ExtractCustomTypeGeneric<Arr[K]>
}
type ExtractCustomTypeGeneric<A> = A extends CustomType<infer T> ? T : never;
type CustomType<T> = {
name: string,
foo: number,
bar: T
}
This gives me an array object (since K in keyof Arr
are index numbers), whereas I would want the property keys to be name
. Unfortunately something like [Arr[K]['name'] for K in keyof Arr]
isn't something that works.
Upvotes: 1
Views: 734
Reputation: 250376
You can get a union of all the keys in the array using Arr[number]['name']
, then you can use Extract
to extract from the union of all possible elements in the array (Arr[number]
) just the items that have the currently mapped name
:
type ArrayToObject<Arr extends Array<CustomType<any>> = []> = {
[K in Arr[number]['name']]: ExtractCustomTypeGeneric<Extract<Arr[number], { name: K }>>
}
type ExtractCustomTypeGeneric<A> = A extends CustomType<infer T> ? T : never;
type CustomType<T, K = string> = {
name: K,
bar: T
}
function createItems<T extends Array<CustomType<any, K>>, K extends string>(...a: T) : T{
return a;
}
let a = createItems({ name: "A", bar: ""}, { name: "B", bar: 0})
type R = ArrayToObject<typeof a>
//Same as
type R = {
A: string;
B: number;
}
Upvotes: 3