or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

utilities.mddocs/

Utilities

Helper functions and enums for object type detection, property manipulation, and merge operation support.

Capabilities

Object Type Detection

Utility function to determine the type of an object for merge strategy selection.

/**
 * Get the type of the given object.
 * @param object - The object to get the type of.
 * @returns The type of the given object.
 */
function getObjectType(object: unknown): ObjectType;

/**
 * The different types of objects deepmerge-ts supports.
 * Exported as a type (const enum values).
 */
type ObjectType = 0 | 1 | 2 | 3 | 4 | 5;

// Const enum values for reference:
// NOT = 0      - Not an object (null, undefined, primitives)  
// RECORD = 1   - Plain object/record
// ARRAY = 2    - Array
// SET = 3      - Set
// MAP = 4      - Map  
// OTHER = 5    - Other object types (functions, classes, etc.)

Usage Examples:

import { getObjectType } from "deepmerge-ts";

// Primitive types  
console.log(getObjectType(42)); // 0 (NOT)
console.log(getObjectType("hello")); // 0 (NOT) 
console.log(getObjectType(null)); // 0 (NOT)
console.log(getObjectType(undefined)); // 0 (NOT)

// Object types
console.log(getObjectType({})); // 1 (RECORD)
console.log(getObjectType({ a: 1 })); // 1 (RECORD)
console.log(getObjectType([])); // 2 (ARRAY)
console.log(getObjectType([1, 2, 3])); // 2 (ARRAY)
console.log(getObjectType(new Set())); // 3 (SET)
console.log(getObjectType(new Map())); // 4 (MAP)

// Other types
console.log(getObjectType(() => {})); // 5 (OTHER)
console.log(getObjectType(new Date())); // 5 (OTHER)
console.log(getObjectType(/regex/)); // 5 (OTHER)

// Custom usage in merge logic (using const enum values)
const ObjectType = {
  NOT: 0,
  RECORD: 1, 
  ARRAY: 2,
  SET: 3,
  MAP: 4,
  OTHER: 5
} as const;

function customMergeStrategy(obj1: unknown, obj2: unknown) {
  const type1 = getObjectType(obj1);
  const type2 = getObjectType(obj2);
  
  if (type1 === ObjectType.ARRAY && type2 === ObjectType.ARRAY) {
    return [...(obj1 as unknown[]), ...(obj2 as unknown[])];
  }
  
  if (type1 === ObjectType.RECORD && type2 === ObjectType.RECORD) {
    return { ...(obj1 as object), ...(obj2 as object) };
  }
  
  return obj2; // Use second value for other types
}

Property Key Extraction

Utility function for extracting all enumerable property keys, including symbols.

/**
 * Get the keys of the given objects including symbol keys.
 * Note: Only keys to enumerable properties are returned.
 * @param objects - An array of objects to get the keys of.
 * @returns A set containing all the keys of all the given objects.
 */
function getKeys(objects: ReadonlyArray<object>): Set<PropertyKey>;

Usage Examples:

import { getKeys } from "deepmerge-ts";

// Basic usage
const obj1 = { a: 1, b: 2 };
const obj2 = { b: 3, c: 4 };
const keys = getKeys([obj1, obj2]);
console.log(Array.from(keys)); // ["a", "b", "c"]

// With symbol keys
const symbolKey = Symbol("special");
const obj3 = { name: "test", [symbolKey]: "value" };
const obj4 = { name: "other", id: 123 };
const allKeys = getKeys([obj3, obj4]);
console.log(Array.from(allKeys)); // ["name", Symbol(special), "id"]

// Empty objects
const emptyKeys = getKeys([{}, {}]);
console.log(Array.from(emptyKeys)); // []

// Mixed object types
const mixed = [
  { a: 1 },
  { b: 2, [Symbol.iterator]: function* () {} },
  { c: 3 }
];
const mixedKeys = getKeys(mixed);
// Returns all enumerable keys from all objects

// Practical usage in custom merge function
function customRecordMerger(records: object[]) {
  const result = {};
  const allKeys = getKeys(records);
  
  for (const key of allKeys) {
    const values = records
      .filter(record => key in record)
      .map(record => record[key as keyof typeof record]);
    
    if (values.length > 0) {
      result[key as keyof typeof result] = values[values.length - 1];
    }
  }
  
  return result;
}

Property Enumeration Check

Utility function to check if an object has a specific enumerable property.

/**
 * Does the given object have the given property.
 * @param object - The object to test.
 * @param property - The property to test.
 * @returns Whether the object has the property.
 */
function objectHasProperty(
  object: object,
  property: PropertyKey,
): boolean;

Usage Examples:

import { objectHasProperty } from "deepmerge-ts";

// Basic usage
const obj = { name: "Alice", age: 30 };
console.log(objectHasProperty(obj, "name")); // true
console.log(objectHasProperty(obj, "height")); // false

// Symbol properties
const symbolKey = Symbol("id");
const objWithSymbol = { [symbolKey]: 123, visible: true };
console.log(objectHasProperty(objWithSymbol, symbolKey)); // true
console.log(objectHasProperty(objWithSymbol, "visible")); // true

// Non-enumerable properties are not detected
const objWithNonEnum = {};
Object.defineProperty(objWithNonEnum, "hidden", {
  value: "secret",
  enumerable: false
});
console.log(objectHasProperty(objWithNonEnum, "hidden")); // false

// Arrays
const arr = ["a", "b", "c"];
console.log(objectHasProperty(arr, "0")); // true
console.log(objectHasProperty(arr, 0)); // true (numeric keys work)
console.log(objectHasProperty(arr, "length")); // false (length is not enumerable)

// Practical usage in merge operations
function safePropertyMerge(target: object, source: object, key: PropertyKey) {
  if (objectHasProperty(source, key)) {
    const sourceValue = source[key as keyof typeof source];
    
    if (objectHasProperty(target, key)) {
      // Both objects have the property - merge them
      const targetValue = target[key as keyof typeof target];
      return mergeValues(targetValue, sourceValue);
    } else {
      // Only source has the property - copy it
      return sourceValue;
    }
  }
  
  // Source doesn't have the property - keep target value
  return target[key as keyof typeof target];
}

// Filter objects by property existence
function filterObjectsByProperty(objects: object[], property: PropertyKey) {
  return objects.filter(obj => objectHasProperty(obj, property));
}

const data = [
  { name: "Alice", email: "alice@example.com" },
  { name: "Bob" },
  { name: "Charlie", email: "charlie@example.com", phone: "123-456-7890" }
];

const withEmail = filterObjectsByProperty(data, "email");
console.log(withEmail.length); // 2

const withPhone = filterObjectsByProperty(data, "phone");
console.log(withPhone.length); // 1

Type Utilities

Additional utility types and functions for advanced usage scenarios.

/**
 * Get an iterable object that iterates over the given iterables.
 * @param iterables - Array of iterables to chain together
 * @returns Combined iterable that yields values from all input iterables
 */
function getIterableOfIterables<T>(
  iterables: ReadonlyArray<Readonly<Iterable<T>>>,
): Iterable<T>;

Usage Examples:

import { getIterableOfIterables } from "deepmerge-ts";

// Combine multiple iterables
const arrays = [[1, 2], [3, 4], [5, 6]];
const combined = getIterableOfIterables(arrays);

for (const value of combined) {
  console.log(value); // 1, 2, 3, 4, 5, 6
}

// Convert to array
const result = Array.from(combined); // [1, 2, 3, 4, 5, 6]

// Works with different iterable types
const sets = [new Set([1, 2]), new Set([3, 4])];
const maps = [new Map([["a", 1]]), new Map([["b", 2]])];
const strings = ["hello", "world"];

const combinedSets = Array.from(getIterableOfIterables(sets)); // [1, 2, 3, 4]
const combinedMaps = Array.from(getIterableOfIterables(maps)); // [["a", 1], ["b", 2]]
const combinedStrings = Array.from(getIterableOfIterables(strings)); // ["h", "e", "l", "l", "o", "w", "o", "r", "l", "d"]

// Practical usage in custom merge functions
function mergeIterables<T>(iterables: ReadonlyArray<Iterable<T>>): T[] {
  return Array.from(getIterableOfIterables(iterables));
}

// Custom Set merge using the utility
function customSetMerge<T>(sets: ReadonlyArray<ReadonlySet<T>>): Set<T> {
  const combined = getIterableOfIterables(sets);
  return new Set(combined);
}

const set1 = new Set([1, 2, 3]);
const set2 = new Set([3, 4, 5]);
const set3 = new Set([5, 6, 7]);
const mergedSet = customSetMerge([set1, set2, set3]);
console.log(Array.from(mergedSet)); // [1, 2, 3, 4, 5, 6, 7]

Integration with Merge Operations

These utilities are used internally by deepmerge-ts but can also be leveraged in custom merge functions:

import { 
  getObjectType, 
  ObjectType, 
  getKeys, 
  objectHasProperty,
  deepmergeCustom 
} from "deepmerge-ts";

// Example: Smart merge that handles different types intelligently
const smartMerge = deepmergeCustom({
  mergeRecords: (records, utils, meta) => {
    const result = {};
    const allKeys = getKeys(records);
    
    for (const key of allKeys) {
      const values = records
        .filter(record => objectHasProperty(record, key))
        .map(record => record[key]);
      
      if (values.length === 1) {
        result[key] = values[0];
      } else if (values.length > 1) {
        // Determine merge strategy based on value types
        const firstType = getObjectType(values[0]);
        const allSameType = values.every(v => getObjectType(v) === firstType);
        
        if (allSameType && firstType === ObjectType.ARRAY) {
          // Concatenate arrays
          result[key] = values.flat();
        } else if (allSameType && firstType === ObjectType.RECORD) {
          // Recursively merge objects
          result[key] = utils.deepmerge(values);
        } else {
          // Use last value for mixed or primitive types
          result[key] = values[values.length - 1];
        }
      }
    }
    
    return result;
  }
});

// Usage
const result = smartMerge(
  { 
    arrays: [1, 2], 
    objects: { a: 1 }, 
    mixed: "string",
    numbers: 42 
  },
  { 
    arrays: [3, 4], 
    objects: { b: 2 }, 
    mixed: 123,
    numbers: 84 
  }
);
// Result: {
//   arrays: [1, 2, 3, 4],
//   objects: { a: 1, b: 2 },
//   mixed: 123,
//   numbers: 84
// }