TypeScript Deep Merge Utility
ts-deepmerge is a TypeScript utility library providing a robust deep merge function. It automatically infers the return type based on input objects without mutating the source objects, a key differentiator from many mutable merge utilities. Objects and arrays are merged recursively, while primitive values like numbers and strings are overwritten. Merging occurs in the order of arguments provided, giving precedence to later arguments. The current stable version is 7.0.3. As a utility library, its release cadence is driven by new features, bug fixes, or TypeScript version updates rather than a strict schedule. It differentiates itself by strong TypeScript type inference and non-mutation by default, supporting both ESM and CommonJS environments. It also offers an `options` mechanism to customize merge behavior, such as how arrays are handled.
Common errors
-
Type 'Partial<IObj>' is not assignable to type 'IObj'. Property 'b' is missing in type 'Partial<IObj>' but required in type 'IObj'.
cause This is a TypeScript error when trying to assign a `Partial<IObj>` directly to `IObj`.fixThis specific error isn't directly related to `ts-deepmerge`'s return type inference but rather how you declare your input objects. Ensure your input objects conform to the expected types, or if you're merging a `Partial` into a complete type, use a type assertion on the *result* of the merge if `ts-deepmerge`'s inference produces a union type that's too broad. Example: `const result = merge(obj1, obj2) as IObj;` -
TS2345: Argument of type 'string[] | number[]' is not assignable to parameter of type 'string[]'.
cause This indicates an issue with type inference where `ts-deepmerge` might produce a union type for an array (e.g., `(string | number)[]` or `string[] | number[]`) when a more specific type is expected downstream.fixIf you know the final type of the array after merging, use a type assertion on the result of the `merge` call: `const result = merge(obj1, obj2) as { myKey: string[] };` or `const myArray = merge(..., { myArray: [1,2] }) as number[];`.
Warnings
- gotcha When merging objects with generic declared types/interfaces, TypeScript's inference for `ts-deepmerge` may result in a union type that doesn't perfectly reflect the final merged type, especially if properties are known to be overwritten. This is a current limitation with TypeScript's type inference for functions accepting an infinite number of arguments.
- gotcha By default, `ts-deepmerge` merges arrays by concatenating them. If you intend for arrays to be overwritten instead of merged, you must explicitly use the `withOptions` method and set `mergeArrays: false`.
- gotcha Primitive values (numbers, strings, booleans, null, undefined) are always overwritten in the order of arguments, not merged. Only objects and arrays are deeply merged. Ensure this behavior aligns with expectations for your data structures.
Install
-
npm install ts-deepmerge -
yarn add ts-deepmerge -
pnpm add ts-deepmerge
Imports
- merge
const { merge } = require('ts-deepmerge');import { merge } from 'ts-deepmerge'; - merge.withOptions
import { withOptions } from 'ts-deepmerge';import { merge } from 'ts-deepmerge'; const result = merge.withOptions(...);
Quickstart
import { merge } from 'ts-deepmerge';
const obj1 = {
a: {
a: 1
},
array: [1, 2]
};
const obj2 = {
b: {
a: 2,
b: 2
},
array: [3, 4]
};
const obj3 = {
a: {
b: 3
},
b: {
b: 3,
c: 3
},
c: 3,
array: [5, 6]
};
// Basic deep merge
const resultBasic = merge(obj1, obj2, obj3);
console.log('Merged Result (default):', resultBasic);
/*
{ a: { a: 1, b: 3 }, b: { a: 2, b: 3, c: 3 }, array: [ 1, 2, 3, 4, 5, 6 ], c: 3 }
*/
// Merge with options, e.g., to overwrite arrays instead of merging them
const resultWithOptions = merge.withOptions(
{ mergeArrays: false },
obj1,
obj2
);
console.log('Merged Result (mergeArrays: false):', resultWithOptions);
/*
{ a: { a: 1 }, array: [ 3, 4 ] }
*/