Skip to content

Commit

Permalink
fix: adjust deepMerge to handle arrays & objects properyl
Browse files Browse the repository at this point in the history
chore: wip

chore: wip
  • Loading branch information
chrisbbreuer committed Nov 18, 2024
1 parent 440f0f6 commit 88171a9
Showing 1 changed file with 43 additions and 37 deletions.
80 changes: 43 additions & 37 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,61 +3,67 @@ import type { DeepMerge, SimplifyDeep } from './types'
/**
* Deep Merge
*
* Merges arrays if both configs are arrays, otherwise does object deep merge.
* Merges two objects or arrays deeply.
*
* @param target - The target object.
* @param sources - The source objects.
* @param target - The target object (default config).
* @param source - The source objects (loaded configs that should override defaults).
* @returns The merged object.
* @example ```ts
* deepMerge({ foo: 'bar' }, { bar: 'baz' })
* deepMerge([{ foo: 'bar' }], [{ bar: 'baz' }])
* deepMerge({ foo: 'bar' }, [{ foo: 'baz' }])
* ```
*/
export function deepMerge<T, S>(target: T, ...sources: S[]): T extends object
export function deepMerge<T, S>(target: T, source: S): T extends any[]
? S extends any[]
? S
: S extends object
? SimplifyDeep<DeepMerge<T, S>>
: T
: T extends any[]
? Array<SimplifyDeep<DeepMerge<T[number], S[number]>>>
: S
: T extends object
? S extends any[]
? T
: T
? S
: S extends object
? SimplifyDeep<DeepMerge<T, S>>
: T
: T {
if (!sources.length)
return target as any
// If source is an array and target isn't, return source
if (Array.isArray(source) && !Array.isArray(target)) {
return source as any
}

const source = sources.shift()
if (!source)
return target as any
// If both are arrays, merge their contents
if (Array.isArray(source) && Array.isArray(target)) {
return source.map((sourceItem, index) => {
const targetItem = target[index]
if (isObject(sourceItem) && isObject(targetItem)) {
return deepMerge(targetItem, sourceItem)
}
return sourceItem
}) as any
}

if (Array.isArray(source) !== Array.isArray(target)
|| isObject(source) !== isObject(target)) {
// Handle non-objects (primitives)
if (!isObject(source) || !isObject(target)) {
return source as any
}

if (Array.isArray(target) && Array.isArray(source)) {
return [...target, ...source] as any
}
// Handle objects
const merged = { ...target } as any

if (isObject(target) && isObject(source)) {
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const sourceValue = source[key]
if (!Object.prototype.hasOwnProperty.call(target, key)) {
(target as any)[key] = sourceValue
continue
}
for (const key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
const sourceValue = source[key]
const targetValue = merged[key]

(target as any)[key] = deepMerge((target as any)[key], sourceValue)
if (sourceValue === null || sourceValue === undefined) {
merged[key] = sourceValue
}
else if (isObject(sourceValue) && isObject(targetValue)) {
merged[key] = deepMerge(targetValue, sourceValue)
}
else {
merged[key] = sourceValue
}
}
}

return deepMerge(target, ...sources)
return merged
}

function isObject(item: unknown): item is Record<string, unknown> {
return (item && typeof item === 'object' && !Array.isArray(item)) as boolean
return Boolean(item && typeof item === 'object' && !Array.isArray(item))
}

0 comments on commit 88171a9

Please sign in to comment.