Issue
I would like to create a type called X
that accepts a generic type and will be inferred as follows:
- If a parent has only one child, then return itself.
- If not, return
parent name (child 1)
orparent name (child 2)
and so on.
Given the following type:
type ParentsWithChildren= {
parent1: {
child1: unknown;
child2: unknown;
},
parent2: {
child3: unknown;
}
}
// X<ParentsWithChildren> = "parent1 (child1)" | "parent1 (child2)" | "parent2"
Solution
Shout out to @captain-yossarian for nudging me in the right direction.
Here's a simpler approach.
For each property key in the top-most ("parent") object, get the child object, derive the set of its keys. If the set is just one key (i.e., is a string literal type), resolve to the current parent key (it's easy to confuse the two keys here, read carefully). Otherwise, if the set of child object's keys contains multiple keys (i.e., is a union), append each child key from the set to the current parent key.
This is IMO better than @captain-yossarian's answer, because:
- the result perfectly fits the original requirement that children objects with only one property must not include the property key (e.g., it produces
"parent2"
rather than);"parent2 (child3)"
- as far as I can tell, there should be no significant performance caveats that you have to consider, – just be reasonable as usual.
Here's how it might be done (see more about UnionToIntersection
and IsUnion
):
type UnionToIntersection<Union> =
(Union extends any ? (k: Union) => void : never) extends ((k: infer Intersection) => void)
? Intersection
: never;
type IsUnion<Type> = [Type] extends [UnionToIntersection<Type>] ? false : true;
type X<Obj extends Record<string, object>> = {
[Key in keyof Obj]:
IsUnion<keyof Obj[Key]> extends false ?
Key
:
IsUnion<keyof Obj[Key]> extends true ?
`${string & Key} (${string & keyof Obj[Key]})`
:
never;
}[keyof Obj];
Answered By - Dima Parzhitsky
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.