If the discrimated union is defined as follows:
enum KEYS {
A= 'a',
B= 'b'
...
}
type TUnion =
| { keyType: KEYS.A; items: AType[]
| { keyType: KEYS.B; items: BType[]
...
How can I define the type for an object where the key is a keyType
and the value is the matching ìtems`, without doing it all manually?
The manual approach seems to work but repeats the key-to-possible values relation:
type TObj = {
[KEYS.A]?: AType[]
[KEYS.B]?: BType[]
...
}
CodePudding user response:
Here's a great utility type to take a discriminated union and two of its properties and transform it into a map of the keys to values:
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? { [K in keyof I]: I[K] } : never;
type FromDiscriminatedUnion<U, K extends keyof U, V extends keyof U> = UnionToIntersection<U extends U ? {
[_ in U[K] & PropertyKey]: U[V];
} : never>;
We distribute over the union and get the selected key and map it to a value. Since this produces another union, we need to turn this union into an intersection.
Lastly, if you want all the properties to be optional, wrap the output with Partial
:
type Result = Partial<FromDiscriminatedUnion<TUnion, "keyType", "items">>;
CodePudding user response:
Similar idea to caTS's, but a bit simpler: tsplayground
enum KEYS {
A = "a",
B = "b",
}
type AType = number;
type BType = string;
type TUnion = { keyType: KEYS.A; items: AType[] } | { keyType: KEYS.B; items: BType[] };
type UnionToIntersection<U> =
(U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
type ToKeyValue<U extends TUnion> = U extends TUnion ? { [key in U['keyType']]: U['items']} : never;
type mapped = UnionToIntersection<ToKeyValue<TUnion>>
type optionalMapped = Partial<UnionToIntersection<ToKeyValue<TUnion>>>
CodePudding user response:
It makes more sense to define your types the other way around: declare TObj
first, and then you can derive your discriminated union from it.
type TObj = {
[KEYS.A]?: AType[]
[KEYS.B]?: BType[]
}
type TUnion = {
[K in KEYS]: {keyType: K, items: Exclude<TObj[K], undefined>}
}[KEYS]
// type TUnion = {
// keyType: KEYS.A;
// items: AType[];
// } | {
// keyType: KEYS.B;
// items: BType[];
// }