|
1 | 1 | type Key = string | number | symbol; |
2 | | -type GenericObject<A> = Record<Key, A>; |
| 2 | +// eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 3 | +type IsObject<T> = T extends object ? (T extends Array<any> ? false : true) : false; |
| 4 | +// eslint-disable-next-line @typescript-eslint/no-explicit-any |
| 5 | +type GenericObject = object | Array<any>; |
| 6 | +type Merge<T, U> = IsObject<T> & IsObject<U> extends true |
| 7 | + ? { |
| 8 | + [K in keyof T]: K extends keyof U ? Merge<T[K], U[K]> : T[K]; |
| 9 | + } & U |
| 10 | + : U; |
3 | 11 |
|
4 | 12 | class ObjectFunctor<T extends Record<PropertyKey, unknown>> { |
5 | 13 | public constructor(private readonly internal: T) {} |
@@ -47,24 +55,31 @@ class ObjectFunctor<T extends Record<PropertyKey, unknown>> { |
47 | 55 | } |
48 | 56 | } |
49 | 57 |
|
50 | | -function isRecord(value: unknown): value is GenericObject<unknown> { |
51 | | - return typeof value === "object" && value !== null && !Array.isArray(value); |
| 58 | +function isRecord<T>(v: T): IsObject<T> { |
| 59 | + return (typeof v === "object" && v !== null && !Array.isArray(v)) as IsObject<T>; |
52 | 60 | } |
53 | 61 |
|
54 | | -function deepMerge<A, B>(a: GenericObject<A>, b: GenericObject<B>): GenericObject<A | B> { |
55 | | - const result: GenericObject<A | B> = { ...a }; |
56 | | - |
57 | | - for (const [key, value] of Object.entries(b)) { |
58 | | - if (isRecord(result[key]) && isRecord(value)) { |
59 | | - result[key] = deepMerge(result[key], value) as A | B; |
60 | | - } else if (Array.isArray(result[key]) && Array.isArray(value)) { |
61 | | - result[key] = [...result[key], ...value] as A | B; |
62 | | - } else { |
63 | | - result[key] = value; |
64 | | - } |
65 | | - } |
66 | | - |
67 | | - return result; |
| 62 | +function deepMerge<T extends object, U extends object>( |
| 63 | + a: T, |
| 64 | + b: U, |
| 65 | + { mergeArrays = true }: { mergeArrays?: boolean } = {}, |
| 66 | +): Merge<T, U> { |
| 67 | + return ( |
| 68 | + isRecord(a) && isRecord(b) |
| 69 | + ? Object.assign( |
| 70 | + {}, |
| 71 | + a, |
| 72 | + Object.fromEntries( |
| 73 | + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access |
| 74 | + Object.entries(b).map(([k, v]) => [k, deepMerge((a as any)[k], v, { mergeArrays })]), |
| 75 | + ), |
| 76 | + ) |
| 77 | + : Array.isArray(a) && Array.isArray(b) |
| 78 | + ? mergeArrays |
| 79 | + ? [...a, ...b] |
| 80 | + : b |
| 81 | + : b |
| 82 | + ) as Merge<T, U>; |
68 | 83 | } |
69 | 84 |
|
70 | 85 | export { deepMerge, isRecord, ObjectFunctor }; |
|
0 commit comments