I’ve got a type a such:
type OneOf =
| {
$case: 'one';
one: One;
}
| {
$case: 'two';
two: Two;
}
| {
$case: 'three';
three: Three;
};
I want to create a new type to be able to get from the input OneOf, get the output One | Two | Three.
I have tried many different things, including one from this answer with UnionToIntersection in the hope that with an intersection, I could simply exclude the $case, loop on the keys and return the type.
I always get a never type as output though.
Essentially, I thought I could get away with it with the following:
type ExtractOneOfTypes<T, K extends keyof T = keyof T> = {
[key in K]: T extends { $case: key } ? T[key] : never;
}[K];
Because in my oneOf, for each $case, on that same object there is a property name that is the same as the $case value. But this doesn’t work. Any ideas?
>Solution :
You you can infer the literal value type of $case and then check if it’s a keyof T using a nested Conditional Type like so:
type One = { one: string };
type Two = { two: number };
type Three = { three: boolean };
type OneOf =
| { $case: "one"; one: One }
| { $case: "two"; two: Two }
| { $case: "three"; three: Three };
type ExtractOneOfTypes<T, K extends keyof T> = T extends { [Key in K]: infer U }
? U extends keyof T
? T[U]
: never
: never;
type ExtractedType = ExtractOneOfTypes<OneOf, "$case">;
// ^? type ExtractedType = One | Two | Three