Created
January 7, 2025 19:29
-
-
Save Danetag/7300ad41c09d4bc731c6ea5d78d7038f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {describe, it, expect} from 'vitest'; | |
import {merge} from './deepMerge'; | |
describe('deepMerge', () => { | |
it('should merge flat objects', () => { | |
const obj1 = {a: 1, b: 2}; | |
const obj2 = {c: 3, d: 4}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
a: 1, | |
b: 2, | |
c: 3, | |
d: 4, | |
}); | |
}); | |
it('should override primitive values', () => { | |
const obj1 = {a: 1, b: 'old'}; | |
const obj2 = {b: 'new'}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
a: 1, | |
b: 'new', | |
}); | |
}); | |
it('should merge nested objects', () => { | |
const obj1 = { | |
a: { | |
b: 1, | |
c: 2, | |
}, | |
}; | |
const obj2 = { | |
a: { | |
c: 3, | |
d: 4, | |
}, | |
}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
a: { | |
b: 1, | |
c: 3, | |
d: 4, | |
}, | |
}); | |
}); | |
it('should replace arrays instead of merging them', () => { | |
const obj1 = { | |
arr: [1, 2, 3], | |
}; | |
const obj2 = { | |
arr: [4, 5, 6], | |
}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
arr: [4, 5, 6], | |
}); | |
}); | |
it('should handle null values', () => { | |
const obj1 = { | |
a: null, | |
b: 1, | |
}; | |
const obj2 = { | |
b: null, | |
}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
a: null, | |
b: null, | |
}); | |
}); | |
it('should merge multiple objects', () => { | |
const obj1 = {a: 1}; | |
const obj2 = {b: 2}; | |
const obj3 = {c: 3}; | |
const result = merge({}, obj1, obj2, obj3); | |
expect(result).toEqual({ | |
a: 1, | |
b: 2, | |
c: 3, | |
}); | |
}); | |
it('should handle complex nested structures', () => { | |
const obj1 = { | |
config: { | |
api: { | |
endpoint: 'http://old.api.com', | |
timeout: 1000, | |
}, | |
features: ['a', 'b'], | |
settings: { | |
theme: 'dark', | |
notifications: { | |
email: true, | |
push: false, | |
}, | |
}, | |
}, | |
}; | |
const obj2 = { | |
config: { | |
api: { | |
endpoint: 'http://new.api.com', | |
}, | |
features: ['c'], | |
settings: { | |
notifications: { | |
push: true, | |
}, | |
}, | |
}, | |
}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({ | |
config: { | |
api: { | |
endpoint: 'http://new.api.com', | |
timeout: 1000, | |
}, | |
features: ['c'], | |
settings: { | |
theme: 'dark', | |
notifications: { | |
email: true, | |
push: true, | |
}, | |
}, | |
}, | |
}); | |
}); | |
it('should not modify source objects', () => { | |
const obj1 = {a: {b: 1}}; | |
const obj2 = {a: {c: 2}}; | |
const original1 = JSON.parse(JSON.stringify(obj1)); | |
const original2 = JSON.parse(JSON.stringify(obj2)); | |
merge({}, obj1, obj2); | |
expect(obj1).toEqual(original1); | |
expect(obj2).toEqual(original2); | |
}); | |
it('should handle empty objects', () => { | |
const obj1 = {a: 1}; | |
const obj2 = {}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({a: 1}); | |
}); | |
it('should handle undefined values', () => { | |
const obj1 = {a: undefined, b: 1}; | |
const obj2 = {b: 2}; | |
const result = merge({}, obj1, obj2); | |
expect(result).toEqual({a: undefined, b: 2}); | |
}); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* A custom and simple `lodash/merge`, focused on deep merging JSON objects. | |
*/ | |
type JsonPrimitive = string | number | boolean | null | undefined; | |
type JsonArray = JsonValue[]; | |
type JsonObject = {[key: string]: JsonValue}; | |
type JsonValue = JsonPrimitive | JsonObject | JsonArray; | |
/** | |
* Deep merges multiple JSON objects, preserving type information | |
*/ | |
export function merge<First extends JsonObject, Rest extends JsonObject[]>( | |
first: First, | |
...rest: Rest | |
): First & UnionToIntersection<Rest[number]> { | |
const target = {...first} as JsonObject; | |
for (const source of rest) { | |
for (const key in source) { | |
const sourceValue = source[key]; | |
const targetValue = target[key]; | |
if (isObject(sourceValue) && isObject(targetValue)) { | |
target[key] = merge(targetValue, sourceValue); | |
} else if (Array.isArray(sourceValue)) { | |
target[key] = [...sourceValue]; | |
} else { | |
target[key] = sourceValue; | |
} | |
} | |
} | |
return target as First & UnionToIntersection<Rest[number]>; | |
} | |
type UnionToIntersection<U> = ( | |
U extends unknown ? (k: U) => void : never | |
) extends (k: infer I) => void | |
? I | |
: never; | |
function isObject(value: unknown): value is JsonObject { | |
return typeof value === 'object' && value !== null && !Array.isArray(value); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment