Skip to content

Commit

Permalink
fix: improve types further for even more accuracy and readability 🛠
Browse files Browse the repository at this point in the history
  • Loading branch information
mesqueeb committed Nov 14, 2022
1 parent f164be7 commit b1cd0da
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 84 deletions.
41 changes: 16 additions & 25 deletions dist/types/typeUtils/MergeDeep.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ declare type RequiredKeys<O extends object> = O extends unknown ? {
[K in keyof O]-?: {} extends Pick<O, K> ? never : K;
}[keyof O] : never;
declare type MergeObjectDeeply<O extends Record<string | number | symbol, unknown>, O1 extends Record<string | number | symbol, unknown>> = {
[K in keyof (O & O1)]: K extends RequiredKeys<O1> ? O1[K] : K extends OptionalKeys<O1> ? K extends OptionalKeys<O> ? MergeObjectOrReturnUnion<Exclude<O[K], undefined>, Exclude<O1[K], undefined>> : K extends RequiredKeys<O> ? Exclude<O1[K], undefined> extends O[K] ? O[K] : MergeObjectOrReturnUnion<O[K], Exclude<O1[K], undefined>> : O1[K] : O[K];
[K in keyof (O & O1)]: K extends RequiredKeys<O1> ? MergeObjectsOrReturnFallback<O[K], O1[K], O1[K]> : K extends OptionalKeys<O1> ? K extends OptionalKeys<O> ? MergeObjectsOrReturnFallback<Exclude<O[K], undefined>, Exclude<O1[K], undefined>, Exclude<O[K], undefined> | Exclude<O1[K], undefined>> : K extends RequiredKeys<O> ? Exclude<O1[K], undefined> extends O[K] ? O[K] : MergeObjectsOrReturnFallback<O[K], Exclude<O1[K], undefined>, O[K] | Exclude<O1[K], undefined>> : O1[K] : O[K];
};
declare type MergeObjectOrReturnUnion<Val0, Val1> = Val0 extends Record<string | number | symbol, unknown> ? Val1 extends Record<string | number | symbol, unknown> ? MergeObjectDeeply<Val0, Val1> : Val0 | Val1 : Val0 | Val1;
declare type MergeObjectsOrReturnFallback<O, O1, Fallback> = O extends Record<string | number | symbol, unknown> ? O1 extends Record<string | number | symbol, unknown> ? MergeObjectDeeply<O, O1> : Fallback : Fallback;
/**
* Accurately merge the fields of `O` with the ones of `O1`. It is
* equivalent to the spread operator in JavaScript. [[Union]]s and [[Optional]]
Expand All @@ -35,31 +35,22 @@ declare type MergeObjectOrReturnUnion<Val0, Val1> = Val0 extends Record<string |
* @returns [[Object]]
* @example
* ```ts
* type O = {
* name?: string
* age?: number
* zip?: string
* pay: { cvv?: number }
* }
* import { PrettyPrint } from './PrettyPrint'
*
* type O1 = {
* age: number
* zip?: number
* pay: { cvv : number; ccn?: string }
* city: string
* }
* type A1 = { a: number; b?: number; d?: number; e?: number; x: string; y?: number; z: string; } // prettier-ignore
* type A2 = { a?: number; c?: number; d?: number; e: number; x: number | undefined; y?: string; z?: number; } // prettier-ignore
*
* type test = MergeDeep<O, O1>
* // {
* // name?: string;
* // age: number;
* // zip: number | undefined;
* // pay: {
* // cvv: number;
* // ccn?: string;
* // };
* // city: string;
* // }
* type Result = PrettyPrint<MergeDeep<A1, A2>>
* {
* a: number;
* b?: number | undefined;
* c?: number | undefined;
* d?: number | undefined;
* e: number;
* x: number | undefined;
* y?: string | number | undefined;
* z: string | number;
* }
* ```
*/
export declare type MergeDeep<O extends Record<string | number | symbol, unknown>, O1 extends Record<string | number | symbol, unknown>> = O extends unknown ? (O1 extends unknown ? MergeObjectDeeply<O, O1> : never) : never;
Expand Down
97 changes: 38 additions & 59 deletions src/typeUtils/MergeDeep.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { PrettyPrint } from './PrettyPrint'

/**
* Get the keys of `O` that are optional
* @param O
Expand Down Expand Up @@ -35,23 +33,34 @@ type MergeObjectDeeply<
O1 extends Record<string | number | symbol, unknown>
> = {
[K in keyof (O & O1)]: K extends RequiredKeys<O1> // second prop is non-optional
? O1[K] // return second prop
? MergeObjectsOrReturnFallback<O[K], O1[K], O1[K]> // return second prop
: K extends OptionalKeys<O1> // second prop is optional
? K extends OptionalKeys<O> // first prop is optional (second prop also)
? MergeObjectOrReturnUnion<Exclude<O[K], undefined>, Exclude<O1[K], undefined>> // return union
? MergeObjectsOrReturnFallback<
Exclude<O[K], undefined>,
Exclude<O1[K], undefined>,
Exclude<O[K], undefined> | Exclude<O1[K], undefined>
> // return union
: K extends RequiredKeys<O> // first prop required (second prop optional)
? Exclude<O1[K], undefined> extends O[K] // (optional) second prop has the same type as the (required) first prop
? O[K] // return only the first one
: MergeObjectOrReturnUnion<O[K], Exclude<O1[K], undefined>> // (optional) second prop has a different type as the (required) first prop, so return union without `undefined` in the second
: MergeObjectsOrReturnFallback<
O[K],
Exclude<O1[K], undefined>,
O[K] | Exclude<O1[K], undefined>
> // (optional) second prop has a different type as the (required) first prop, so return union without `undefined` in the second
: O1[K] // first prop inexistent, so return second prop
: O[K] // second prop inexistent, so return first prop
}

type MergeObjectOrReturnUnion<Val0, Val1> = Val0 extends Record<string | number | symbol, unknown>
? Val1 extends Record<string | number | symbol, unknown>
? MergeObjectDeeply<Val0, Val1>
: Val0 | Val1
: Val0 | Val1
type MergeObjectsOrReturnFallback<O, O1, Fallback> = O extends Record<
string | number | symbol,
unknown
>
? O1 extends Record<string | number | symbol, unknown>
? MergeObjectDeeply<O, O1>
: Fallback
: Fallback

/**
* Accurately merge the fields of `O` with the ones of `O1`. It is
Expand All @@ -64,64 +73,30 @@ type MergeObjectOrReturnUnion<Val0, Val1> = Val0 extends Record<string | number
* @returns [[Object]]
* @example
* ```ts
* type O = {
* name?: string
* age?: number
* zip?: string
* pay: { cvv?: number }
* }
* import { PrettyPrint } from './PrettyPrint'
*
* type O1 = {
* age: number
* zip?: number
* pay: { cvv : number; ccn?: string }
* city: string
* }
* type A1 = { a: number; b?: number; d?: number; e?: number; x: string; y?: number; z: string; } // prettier-ignore
* type A2 = { a?: number; c?: number; d?: number; e: number; x: number | undefined; y?: string; z?: number; } // prettier-ignore
*
* type test = MergeDeep<O, O1>
* // {
* // name?: string;
* // age: number;
* // zip: number | undefined;
* // pay: {
* // cvv: number;
* // ccn?: string;
* // };
* // city: string;
* // }
* type Result = PrettyPrint<MergeDeep<A1, A2>>
* {
* a: number;
* b?: number | undefined;
* c?: number | undefined;
* d?: number | undefined;
* e: number;
* x: number | undefined;
* y?: string | number | undefined;
* z: string | number;
* }
* ```
*/
export type MergeDeep<
O extends Record<string | number | symbol, unknown>,
O1 extends Record<string | number | symbol, unknown>
> = O extends unknown ? (O1 extends unknown ? MergeObjectDeeply<O, O1> : never) : never

// type O = {
// name?: string
// age?: number
// zip?: string
// pay: { cvv?: number; xyz: RegExp }
// }

// type O1 = {
// age: number
// zip?: number
// pay: { cvv: number; ccn?: string }
// city: string
// }

// type test = MergeDeep<O, O1>
// {
// name?: string;
// age: number;
// zip: number | undefined;
// pay: {
// cvv: number;
// ccn?: string;
// };
// city: string;
// }

// import { PrettyPrint } from './PrettyPrint'
// type A1 = { arr: string[]; barr?: { b: number } }
// type A2 = { arr?: number[]; barr?: { b: number } }
// type TestA = PrettyPrint<MergeDeep<A1, A2>>
Expand All @@ -130,6 +105,10 @@ export type MergeDeep<
// type B2 = { a?: number; c?: number; d?: number; e: number; x: number | undefined; y?: string; z?: number; } // prettier-ignore
// type TestB = PrettyPrint<MergeDeep<B1, B2>>

// type C1 = { info: { time: string; newDate: Date; very: { deep: { prop: boolean } } } }
// type C2 = { info: { date: string; very: { deep: { prop: boolean } } } }
// type TestC = PrettyPrint<MergeDeep<C1, C2>>

// import { Timestamp } from 'firebase/firestore'
// type T1 = { date: Timestamp }
// type T2 = { date: Timestamp }
Expand Down

0 comments on commit b1cd0da

Please sign in to comment.