Core object manipulation functions including deep cloning, merging, and equality comparison with support for complex data types, circular references, and configurable behavior.
Creates a deep copy of any value, object, or array with configurable shallow copying and symbol handling.
/**
* Clone any value, object, or array
* @param obj - The value being cloned
* @param options - Optional settings for cloning behavior
* @returns A deep clone of obj
*/
function clone<T>(obj: T, options?: clone.Options): T;
namespace clone {
interface Options {
/** Clone the object's prototype. Defaults to true */
readonly prototype?: boolean;
/** Include symbol properties. Defaults to true */
readonly symbols?: boolean;
/**
* Shallow clone the specified keys. Can be:
* - Array of dot-separated key paths to shallow copy
* - Array of array-based key paths
* - true to shallow copy all properties
*/
readonly shallow?: string[] | string[][] | boolean;
}
}Usage Examples:
import { clone } from "@hapi/hoek";
// Basic deep cloning
const original = {
w: /^something$/ig,
x: { a: [1, 2, 3], b: 123456, c: new Date() },
y: 'y',
z: new Date()
};
const copy = clone(original);
copy.x.b = 100;
console.log(original.x.b); // 123456 (unchanged)
console.log(copy.x.b); // 100
// Shallow cloning specific keys
const shallowCopy = clone(original, { shallow: ['x'] });
shallowCopy.x.b = 999;
console.log(original.x.b); // 999 (changed - x was shallow copied)Merges all properties of source into target (destructive operation) with configurable null handling and array merging.
/**
* Merge all the properties of source into target
* @param target - The object being modified
* @param source - The object used to copy properties from
* @param options - Optional settings for merge behavior
* @returns The target object with merged properties
*/
function merge<T1 extends object, T2 extends object>(target: T1, source: T2, options?: merge.Options): T1 & T2;
namespace merge {
interface Options {
/** When true, null value from source overrides existing value in target. Defaults to true */
readonly nullOverride?: boolean;
/** When true, array value from source is merged with existing value in target. Defaults to true */
readonly mergeArrays?: boolean;
/** Compare symbol properties. Defaults to undefined */
readonly symbols?: boolean;
}
}Usage Examples:
import { merge } from "@hapi/hoek";
// Basic object merging
const target = { a: 1, b: 2 };
const source = { a: 0, c: 5 };
merge(target, source); // target becomes { a: 0, b: 2, c: 5 }
// Null value handling
const target2 = { a: 1, b: 2 };
const source2 = { a: null, c: 5 };
merge(target2, source2); // { a: null, b: 2, c: 5 }
merge(target2, source2, { nullOverride: false }); // { a: 1, b: 2, c: 5 }
// Array merging
const targetArray = [1, 2, 3];
const sourceArray = [4, 5];
merge(targetArray, sourceArray); // [1, 2, 3, 4, 5]
merge(targetArray, sourceArray, { mergeArrays: false }); // [4, 5]Applies source to a copy of defaults (non-destructive) creating a new object with merged properties.
/**
* Apply source to a copy of the defaults
* @param defaults - An object with the default values to use if options does not contain the same keys
* @param source - The source used to override the defaults
* @param options - Optional settings for application behavior
* @returns A copy of defaults with source keys overriding any conflicts, or null if source is falsy
*/
function applyToDefaults<T extends object>(defaults: Partial<T>, source: Partial<T> | boolean | null, options?: applyToDefaults.Options): Partial<T> | null;
namespace applyToDefaults {
interface Options {
/** When true, null value from source overrides existing value in target. Defaults to false */
readonly nullOverride?: boolean;
/** Shallow clone the specified keys. Can be array of dot-separated or array-based key paths */
readonly shallow?: string[] | string[][];
}
}Usage Examples:
import { applyToDefaults } from "@hapi/hoek";
// Basic usage
const defaults = { host: "localhost", port: 8000 };
const source = { port: 8080 };
const config = applyToDefaults(defaults, source);
// Result: { host: "localhost", port: 8080 }
// Null value handling
const defaults2 = { host: "localhost", port: 8000 };
const source2 = { host: null, port: 8080 };
const config2 = applyToDefaults(defaults2, source2, { nullOverride: true });
// Result: { host: null, port: 8080 }
// Shallow copying specific paths
const defaults3 = {
db: { server: { host: "localhost", port: 8000 }, name: 'example' }
};
const source3 = { db: { server: { port: 8080 } } };
const config3 = applyToDefaults(defaults3, source3, { shallow: ['db.server'] });
// Result: { db: { server: { port: 8080 }, name: 'example' } }Performs deep comparison of two values including support for circular dependencies, prototype comparison, and enumerable properties.
/**
* Performs a deep comparison of the two values including support for circular dependencies, prototype, and enumerable properties
* @param obj - The value being compared
* @param ref - The reference value used for comparison
* @param options - Optional settings for comparison behavior
* @returns true when the two values are equal, otherwise false
*/
function deepEqual(obj: any, ref: any, options?: deepEqual.Options): boolean;
namespace deepEqual {
interface Options {
/** Compare functions with different references by comparing their internal code and properties. Defaults to false */
readonly deepFunction?: boolean;
/** Allow partial match. Defaults to false */
readonly part?: boolean;
/** Compare the objects' prototypes. Defaults to true */
readonly prototype?: boolean;
/** List of object keys to ignore different values of. Defaults to null */
readonly skip?: (string | symbol)[];
/** Compare symbol properties. Defaults to true */
readonly symbols?: boolean;
}
}Usage Examples:
import { deepEqual } from "@hapi/hoek";
// Basic deep comparison
const obj1 = { a: [1, 2], b: 'string', c: { d: true } };
const obj2 = { a: [1, 2], b: 'string', c: { d: true } };
deepEqual(obj1, obj2); // true
// Prototype comparison
deepEqual(Object.create(null), {}, { prototype: false }); // true
deepEqual(Object.create(null), {}); // false
// Partial matching
const partial = { a: 1 };
const full = { a: 1, b: 2, c: 3 };
deepEqual(partial, full, { part: true }); // true
// Skip specific keys
deepEqual(
{ a: 1, b: 2, timestamp: 123 },
{ a: 1, b: 2, timestamp: 456 },
{ skip: ['timestamp'] }
); // true