or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

customization.mdimmutable-merging.mdindex.mdmutable-merging.mdutilities.md
tile.json

immutable-merging.mddocs/

Immutable Deep Merging

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.

Capabilities

Basic Deep Merge

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]

Custom Deep Merge

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
});

Merge Options

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;
}

Default Merge Functions

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>;
};

Type System

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>>;