Is it possible to translate a type of this kind:
export type UnionType = {
element:
| { $case: 'a'; a: number }
| { $case: 'b'; b: string }
| { $case: 'c'; c: boolean };
};
to a type of a kind:
type OneOfConverter<T> = any; // TODO
type ObjectType = OneOfConverter<UnionType>;
// type ObjectType = {
// a: number;
// b: string;
// c: boolean;
// };
CodePudding user response:
My suggestion here would be:
type OneOfConverter<T extends { element: { $case: PropertyKey } }> = {
[U in T["element"] as U["$case"]]: U["$case"] extends keyof U ? U[U["$case"]] : never
}
It uses key remapping in mapped types to iterate over the union members of T['element']
(an indexed access type corresponding to the type of the element
property of the type T
passed in) and put each member of that union into a type parameter U
. Then the key we want is U["$case"]
.
For each value we pretty much want to index into U
with that key, so U[U["$case"]]
, but the compiler doesn't know if that member really will exist (maybe you pass in { $case: "z" }
and the z
property doesn't appear for some reason). So we have to address its concern by first doing the conditional type check U["$case"] extends keyof U
, which compares the desired key to the keys of U
. If it exists, we grab the property value as U[U["$case"]]
. If it doesn't, then we return never
for want of anything better.
Let's make sure it works:
type ObjectType = OneOfConverter<UnionType>;
// type ObjectType = {
// a: number;
// b: string;
// c: boolean;
// };
Looks good!