I’m trying to use the “pick” function of typescript to get all the possible values of my objects. My objects have optional attributes so they are not necessarily set
const foo = [ { name: 'index', }, { name: 'id', type: 'number', }, { name: 'error', }, ] as const type ElementArg<T extends ReadonlyArray<unknown>> = T extends ReadonlyArray<infer ElementType> ? Pick<ElementType, 'name' | 'type'> : never type result = ElementArg<typeof foo> //result : {name: "index" | "id" | "error"} //expected : {name: "index" | "id" | "error" type: "number" | "undefined"}
and I also try to convert “type” attribut to a type with :
type typeDecoder<T> = T extends 'number' ? number : T extends 'number[]' ? number[] : T extends 'string' ? string : T extends 'string[]' ? string[] : T extends 'boolean' ? boolean : T extends 'undefined' ? undefined : never;
but I think there is a better way to do it and I don’t know where to use my function
Advertisement
Answer
I managed to get something to work:
type ElementArg<T extends ReadonlyArray<unknown>, R extends {} = {}> = T extends readonly [infer First, ...infer Rest] ? ElementArg<Rest, { [K in keyof First | keyof R]: K extends keyof R ? K extends keyof First ? R[K] | First[K] : R[K] | "undefined" : K extends keyof First ? First[K] : never }> : R;
The main idea is that we loop through each of the elements in the tuple and then we add to the result we accumulate.
T extends readonly [infer First, ...infer Rest]
Here we get the first element and the rest as a tuple. Next is this big chunk:
ElementArg<Rest, { [K in keyof First | keyof R]: K extends keyof R ? K extends keyof First ? R[K] | First[K] : R[K] | "undefined" : K extends keyof First ? First[K] : never }>
We use ElementArg
again on the remaining elements, and then the long complicated mapped type correctly adds this element’s contents to the results.
The logic is like this:
For each key in the element or result
If the key is a key of the result
If the key is a key of the element
- Add the key’s value to the result
Otherwise
- Add
"undefined"
to the result
- Add
Or else if the key is a key of the element
- This is a new key we add to the result
And finally if the first step where we attempt to get the first element does not work, that means T
is empty and we are done, so we return R
.