mergeDeep
Merges two or more objects deeply, returning a new object without mutating any input.
Sources are applied left to right. For each key:
- If both sides are plain objects → merged recursively into a new object.
- Otherwise → later source wins (the earlier value is overwritten).
undefinedin a source never overwrites an existing value.nullin a source does overwrite and prevents a later source from deep-merging into that key:mergeDeep({ a: { x: 1 } }, { a: null }, { a: { y: 2 } })→{ a: { y: 2 } }—x: 1is permanently lost.- Arrays, class instances, and all non-plain-object values are replaced, not merged.
Own enumerable string and symbol keys of each source are processed at the top level;
inherited and non-enumerable properties are skipped. Prototype-polluting keys
(__proto__, constructor, prototype) are silently ignored.
Note: symbol keys inside nested plain-object values are not preserved — they are
lost when those values are deep-cloned. Only top-level symbol keys survive.
TypeScript return type — intersection semantics
The return type is A & B & C … (intersection of all source types). This is accurate
when keys are disjoint or share the same type:
When the same key carries incompatible types across sources, the intersection
resolves to never for that key — TypeScript surfaces the conflict rather than
silently picking a type:
At runtime the later source always wins ('x'), but the never type signals that
the caller should align their source types. If intentional, cast the result.
Available since v1.9.0
Import
Section titled “Import”Signature
Section titled “Signature”Parameters
Section titled “Parameters”| Parameter | Type | Description |
|---|---|---|
sources | [rest] | Two or more objects to merge (none are mutated) |
Returns
Section titled “Returns”MergeResult<T> — A new object that is the deep merge of all sources
Examples
Section titled “Examples”Merge two objects deeply
Section titled “Merge two objects deeply”Returns a new object — neither input is mutated.
