Immutable deep merging functionality that creates new objects while preserving the original inputs. Perfect for functional programming patterns, state management, and scenarios where data immutability is required.
Deeply merges 2 or more objects, arrays, Maps, or Sets creating a new result without modifying the input objects.
/**
* Deeply merge objects.
* @param objects - The objects to merge.
* @returns A new merged object with inferred types
*/
function deepmerge<Ts extends Readonly<ReadonlyArray<unknown>>>(
...objects: readonly [...Ts]
): DeepMergeHKT<
Ts,
DeepMergeMergeFunctionsDefaultURIs,
DeepMergeBuiltInMetaData
>;Usage Examples:
import { deepmerge } from "deepmerge-ts";
// Object merging with type inference
const config = deepmerge(
{ api: { timeout: 5000 }, debug: true },
{ api: { retries: 3 }, cache: true }
);
// Result: { api: { timeout: 5000, retries: 3 }, debug: true, cache: true }
// Array merging (concatenation by default)
const combined = deepmerge([1, 2], [3, 4], [5, 6]);
// Result: [1, 2, 3, 4, 5, 6]
// Mixed type merging
const result = deepmerge(
{ users: ["alice"], settings: { theme: "dark" } },
{ users: ["bob"], settings: { lang: "en" } }
);
// Result: { users: ["alice", "bob"], settings: { theme: "dark", lang: "en" } }
// Map merging
const map1 = new Map([["a", 1], ["b", 2]]);
const map2 = new Map([["c", 3], ["a", 10]]);
const mergedMap = deepmerge(map1, map2);
// Result: Map with [["a", 10], ["b", 2], ["c", 3]]
// Set merging (union)
const set1 = new Set([1, 2, 3]);
const set2 = new Set([3, 4, 5]);
const mergedSet = deepmerge(set1, set2);
// Result: Set with [1, 2, 3, 4, 5]Creates a customized merge function with user-defined options for handling different data types and merge strategies.
/**
* Deeply merge two or more objects using the given options.
* @param options - The options on how to customize the merge function.
* @returns A customized merge function
*/
function deepmergeCustom<
BaseTs = unknown,
PMF extends Partial<DeepMergeMergeFunctionsURIs> = {},
>(
options: DeepMergeOptions<DeepMergeBuiltInMetaData, DeepMergeBuiltInMetaData>
): <Ts extends ReadonlyArray<BaseTs>>(
...objects: Ts
) => DeepMergeHKT<
Ts,
GetDeepMergeMergeFunctionsURIs<PMF>,
DeepMergeBuiltInMetaData
>;
/**
* Deeply merge two or more objects using the given options and meta data.
* @param options - The options on how to customize the merge function.
* @param rootMetaData - The meta data passed to the root items' being merged.
* @returns A customized merge function with metadata support
*/
function deepmergeCustom<
BaseTs = unknown,
PMF extends Partial<DeepMergeMergeFunctionsURIs> = {},
MetaData = DeepMergeBuiltInMetaData,
MetaMetaData extends DeepMergeBuiltInMetaData = DeepMergeBuiltInMetaData,
>(
options: DeepMergeOptions<MetaData, MetaMetaData>,
rootMetaData?: MetaData
): <Ts extends ReadonlyArray<BaseTs>>(
...objects: Ts
) => DeepMergeHKT<Ts, GetDeepMergeMergeFunctionsURIs<PMF>, MetaData>;Usage Examples:
import { deepmergeCustom } from "deepmerge-ts";
// Custom array merging - replace instead of concatenate
const mergeReplace = deepmergeCustom({
mergeArrays: (arrays) => arrays[arrays.length - 1], // Use last array
});
const result1 = mergeReplace(
{ items: [1, 2, 3] },
{ items: [4, 5] }
);
// Result: { items: [4, 5] }
// Skip merging certain properties
const mergeWithSkip = deepmergeCustom({
mergeRecords: (records, utils, meta) => {
const result = {};
for (const key of utils.getKeys(records)) {
if (key === 'skipMe') {
return utils.actions.skip;
}
const values = records.map(r => r[key]).filter(v => v !== undefined);
if (values.length > 0) {
result[key] = utils.deepmerge(values);
}
}
return result;
}
});
// Disable specific merge types
const mergeNoArrays = deepmergeCustom({
mergeArrays: false, // Arrays will be treated as "other" types
});
const result2 = mergeNoArrays(
{ data: [1, 2] },
{ data: [3, 4] }
);
// Result: { data: [3, 4] } (second array replaces first)
// Enable implicit default merging
const mergeWithFallback = deepmergeCustom({
enableImplicitDefaultMerging: true,
mergeRecords: () => undefined, // Return undefined to fall back to default
});The DeepMergeOptions interface provides comprehensive customization for merge behavior.
/**
* The options the user can pass to customize deepmerge.
*/
type DeepMergeOptions<
in out M,
MM extends Readonly<Record<PropertyKey, unknown>> = {}
> = Partial<{
/** Custom function for merging objects/records */
mergeRecords: DeepMergeMergeFunctions<M, MM>["mergeRecords"] | false;
/** Custom function for merging arrays */
mergeArrays: DeepMergeMergeFunctions<M, MM>["mergeArrays"] | false;
/** Custom function for merging Maps */
mergeMaps: DeepMergeMergeFunctions<M, MM>["mergeMaps"] | false;
/** Custom function for merging Sets */
mergeSets: DeepMergeMergeFunctions<M, MM>["mergeSets"] | false;
/** Custom function for merging other types */
mergeOthers: DeepMergeMergeFunctions<M, MM>["mergeOthers"];
/** Function to update metadata during merging */
metaDataUpdater: (previousMeta: M | undefined, metaMeta: Readonly<Partial<MM>>) => M;
/** Enable falling back to default merge when custom functions return undefined */
enableImplicitDefaultMerging: boolean;
}>;
/**
* Utility interface provided to merge functions
*/
interface DeepMergeMergeFunctionUtils<M, MM> {
/** Default merge function implementations */
defaultMergeFunctions: DeepMergeMergeFunctionsDefaults;
/** Current merge function configuration */
mergeFunctions: DeepMergeMergeFunctions<M, MM>;
/** Metadata updater function */
metaDataUpdater: (previousMeta: M | undefined, metaMeta: Readonly<Partial<MM>>) => M;
/** Recursive deepmerge function for nested merging */
deepmerge: (...objects: readonly unknown[]) => unknown;
/** Whether implicit default merging is enabled */
useImplicitDefaultMerging: boolean;
/** Special action symbols */
actions: typeof actions;
}The default merge strategies used when no custom options are provided.
/**
* Default merge functions interface
*/
type DeepMergeMergeFunctionsDefaults = {
/** Merge plain objects by recursively merging properties */
mergeRecords: <
Ts extends ReadonlyArray<Record<PropertyKey, unknown>>,
U extends DeepMergeMergeFunctionUtils<M, MM>,
MF extends DeepMergeMergeFunctionsURIs,
M,
MM extends DeepMergeBuiltInMetaData
>(
values: Ts,
utils: U,
meta: M | undefined
) => DeepMergeRecordsDefaultHKT<Ts, MF, M>;
/** Merge arrays by concatenating all elements */
mergeArrays: <Ts extends ReadonlyArray<ReadonlyArray<unknown>>>(
values: Ts
) => DeepMergeArraysDefaultHKT<Ts, never, never>;
/** Merge Sets by creating union of all elements */
mergeSets: <Ts extends ReadonlyArray<ReadonlySet<unknown>>>(
values: Ts
) => DeepMergeSetsDefaultHKT<Ts>;
/** Merge Maps by combining all key-value pairs (later keys override earlier) */
mergeMaps: <Ts extends ReadonlyArray<ReadonlyMap<unknown, unknown>>>(
values: Ts
) => DeepMergeMapsDefaultHKT<Ts>;
/** Merge other types by using the last non-undefined value */
mergeOthers: <Ts extends ReadonlyArray<unknown>>(
values: Ts
) => DeepMergeLeaf<Ts>;
};Advanced TypeScript types used for compile-time type inference and safety.
/**
* Higher-Kinded Type for deep merge result inference
*/
type DeepMergeHKT<
Ts extends ReadonlyArray<unknown>,
MF extends DeepMergeMergeFunctionsURIs,
M
>;
/**
* Extract merge function URIs from partial merge function configuration
*/
type GetDeepMergeMergeFunctionsURIs<PMF extends Partial<DeepMergeMergeFunctionsURIs>>;
/**
* Default HKT implementations for different data structures
*/
type DeepMergeRecordsDefaultHKT<Ts, MF, M>;
type DeepMergeArraysDefaultHKT<Ts, MF, M>;
type DeepMergeSetsDefaultHKT<Ts>;
type DeepMergeMapsDefaultHKT<Ts>;
type DeepMergeLeaf<Ts>;
/**
* Built-in metadata type used in merge operations
*/
type DeepMergeBuiltInMetaData = Readonly<Record<PropertyKey, unknown>>;