@@ -3,61 +3,67 @@ import type { DeepMerge, SimplifyDeep } from './types'
3
3
/**
4
4
* Deep Merge
5
5
*
6
- * Merges arrays if both configs are arrays, otherwise does object deep merge .
6
+ * Merges two objects or arrays deeply .
7
7
*
8
- * @param target - The target object.
9
- * @param sources - The source objects.
8
+ * @param target - The target object (default config) .
9
+ * @param source - The source objects (loaded configs that should override defaults) .
10
10
* @returns The merged object.
11
- * @example ```ts
12
- * deepMerge({ foo: 'bar' }, { bar: 'baz' })
13
- * deepMerge([{ foo: 'bar' }], [{ bar: 'baz' }])
14
- * deepMerge({ foo: 'bar' }, [{ foo: 'baz' }])
15
- * ```
16
11
*/
17
- export function deepMerge < T , S > ( target : T , ... sources : S [ ] ) : T extends object
12
+ export function deepMerge < T , S > ( target : T , source : S ) : T extends any [ ]
18
13
? S extends any [ ]
19
- ? S
20
- : S extends object
21
- ? SimplifyDeep < DeepMerge < T , S > >
22
- : T
23
- : T extends any [ ]
14
+ ? Array < SimplifyDeep < DeepMerge < T [ number ] , S [ number ] > > >
15
+ : S
16
+ : T extends object
24
17
? S extends any [ ]
25
- ? T
26
- : T
18
+ ? S
19
+ : S extends object
20
+ ? SimplifyDeep < DeepMerge < T , S > >
21
+ : T
27
22
: T {
28
- if ( ! sources . length )
29
- return target as any
23
+ // If source is an array but target isn't, return source
24
+ if ( Array . isArray ( source ) && ! Array . isArray ( target ) ) {
25
+ return source as any
26
+ }
30
27
31
- const source = sources . shift ( )
32
- if ( ! source )
33
- return target as any
28
+ // If both are arrays, merge their contents
29
+ if ( Array . isArray ( source ) && Array . isArray ( target ) ) {
30
+ return source . map ( ( sourceItem , index ) => {
31
+ const targetItem = target [ index ]
32
+ if ( isObject ( sourceItem ) && isObject ( targetItem ) ) {
33
+ return deepMerge ( targetItem , sourceItem )
34
+ }
35
+ return sourceItem
36
+ } ) as any
37
+ }
34
38
35
- if ( Array . isArray ( source ) !== Array . isArray ( target )
36
- || isObject ( source ) !== isObject ( target ) ) {
39
+ // Handle non-objects (primitives )
40
+ if ( ! isObject ( source ) || ! isObject ( target ) ) {
37
41
return source as any
38
42
}
39
43
40
- if ( Array . isArray ( target ) && Array . isArray ( source ) ) {
41
- return [ ...target , ...source ] as any
42
- }
44
+ // Handle objects
45
+ const merged = { ...target } as any
43
46
44
- if ( isObject ( target ) && isObject ( source ) ) {
45
- for ( const key in source ) {
46
- if ( Object . prototype . hasOwnProperty . call ( source , key ) ) {
47
- const sourceValue = source [ key ]
48
- if ( ! Object . prototype . hasOwnProperty . call ( target , key ) ) {
49
- ( target as any ) [ key ] = sourceValue
50
- continue
51
- }
47
+ for ( const key in source ) {
48
+ if ( Object . prototype . hasOwnProperty . call ( source , key ) ) {
49
+ const sourceValue = source [ key ]
50
+ const targetValue = merged [ key ]
52
51
53
- ( target as any ) [ key ] = deepMerge ( ( target as any ) [ key ] , sourceValue )
52
+ if ( sourceValue === null || sourceValue === undefined ) {
53
+ merged [ key ] = sourceValue
54
+ }
55
+ else if ( isObject ( sourceValue ) && isObject ( targetValue ) ) {
56
+ merged [ key ] = deepMerge ( targetValue , sourceValue )
57
+ }
58
+ else {
59
+ merged [ key ] = sourceValue
54
60
}
55
61
}
56
62
}
57
63
58
- return deepMerge ( target , ... sources )
64
+ return merged
59
65
}
60
66
61
67
function isObject ( item : unknown ) : item is Record < string , unknown > {
62
- return ( item && typeof item === 'object' && ! Array . isArray ( item ) ) as boolean
68
+ return Boolean ( item && typeof item === 'object' && ! Array . isArray ( item ) )
63
69
}
0 commit comments