In typescript, given 2 object types that has common fields yet are not related, I would like to create a new type where the joined fields are mandatory and the fields that do not exist in both are optional.
How can this be done?
CodePudding user response:
Use DiffAsPartial described below:
export type Union<T1, T2> = T1 & T2;
type KeysOfType<T, SelectedType> = {
[key in keyof T]: SelectedType extends T[key] ? key : never;
}[keyof T];
type PickOptionals<T> = Partial<Pick<T, KeysOfType<T, undefined>>>;
type PickRequired<T> = Omit<T, KeysOfType<T, undefined>>;
export type Intersection<T1, T2> = {
[K in Union<keyof PickOptionals<T1> & string, keyof PickOptionals<T2> & string>]?: PickOptionals<T1>[K] | PickOptionals<T2>[K];
} & {
[K in keyof PickRequired<T1> & keyof PickRequired<T2>]: PickRequired<T1>[K] | PickRequired<T2>[K];
};
export type Diff<T1, T2> = Omit<Union<T1, T2>, keyof Intersection<T1, T2>>;
export type DiffAsPartial<T, S> = Intersection<T, S> & Partial<Diff<T, S>>;
Usage example:
type Type1 = {
a: string,
b?string,
c:number,
}
type Type2 = {
b?string,
c:string,
d: string,
}
type RequiredType = DiffAsPartial<Type1, Type2>;
// == {a?:string, b?:string, c: number | string, d?:string}
CodePudding user response:
Alternative implementation of DiffAsPartial
that may be more readable to some:
// grabs all keys of T that can be undefined
type KeysWithUndefined<T> = { [K in keyof T]: undefined extends T[K] ? K : never }[keyof T];
// excluding keys with undefined from keys of T gives us the keys without undefined
type KeysWithoutUndefined<T> = Exclude<keyof T, KeysWithUndefined<T>>;
// converts keys with undefined to optional keys
type UndefinedToOptional<T> = ({
[K in KeysWithUndefined<T>]?: Exclude<T[K], undefined>;
} & {
[K in KeysWithoutUndefined<T>]: T[K];
}) extends infer O ? { [K in keyof O]: O[K] } : never; // simplify intersection
type DiffAsPartial<A, B> = UndefinedToOptional<{
[K in keyof A | keyof B]: // for all the keys in A and B
// if the key exists in both A and B, then the type is A[K] | B[K]
K extends keyof A & keyof B ? (A | B)[K] :
// if the key exists only in A, then it should be A[K] or undefined
K extends keyof A ? A[K] | undefined :
// if the key exists only in B, then it should be B[K] or undefined
K extends keyof B ? B[K] | undefined :
// this case should never happen
never;
}>;
This is probably slower than @NoyOliel's answer because of the extensive use of conditional types, but for reasonably sized inputs this should not be noticeable.