Begin by extracting all keys present in set A but not in set B, along with keys from set B that are not found in set A. For overlapping keys, define the union explicitly:
type Combine<A, B> =
Omit<A, keyof B> // elements in A not in B
& Omit<B, keyof A> // elements in B not in A
& { [K in keyof A & keyof B]: A[K] | B[K] }; // combination of both.
It is worth noting that specifying that certain keys may be absent in one set or the other can be achieved using Partial
with the Omit
:
type Combine<A, B> =
Partial<Omit<A, keyof B>> // potentially elements in A not in B
& Partial<Omit<B, keyof A>> // possibly elements in B not in A
& { [K in keyof A & keyof B]: A[K] | B[K] }; // combination of both.
Update: Upon Aaronium's inquiry about utilizing unions, an improved approach has been devised, where T extends any ? X : never
is leveraged to expand all keys existing in any element within a union and then consolidate a union of corresponding values across interfaces. This results in a streamlined method:
// retrieves all viable keys from all interfaces within a union.
type AllKeysOf<T> = T extends any ? keyof T : never
// essentially performs T[K], but for unionized T it only yields T[K] for valid key members within the union.
type Get<T, K extends keyof any, Fallback=never> = T extends Record<K, any> ? T[K] : Fallback
// combines a union of interfaces, merging common keys into a union of possibilities.
type Combine<T> = {[K in AllKeysOf<T>]: Get<T,K>}
type TEST = Combine<{a:1} | {a:2, b:10} | {b: 11}>
// TEST = {a: 1|2, b: 10|11}
Keep in mind that adjusting the generic Fallback
in Get</code to <code>never
will exclude union elements lacking that key. Alternatively, setting it to undefined
suggests that keys defined in some interfaces but not all might result in undefined values, resembling optionality. The exact interpretation of these different handling methods largely relies on your specific scenario, so experimentation with both never
and undefined
is recommended to determine desired behavior.