funtool
Version:
A modern, efficient, and modular JavaScript utility library designed to enhance developer productivity.
217 lines (213 loc) • 7.96 kB
TypeScript
/**
* A customizer function for `cloneWith`, used to transform values before cloning.
* - If the customizer returns `undefined`, the default clone logic will be used.
* - If a value is returned, it will be used instead of the default clone.
*/
type Customizer<T> = T extends any[] ? (val: T[number], index: number) => T[number] | undefined : T extends object ? (val: T[keyof T], key: keyof T) => T[keyof T] | undefined : never;
/**
* Creates a shallow clone of the given value.
*
* Supports a wide range of built-in JavaScript types including:
* - Primitives (returned as-is)
* - Arrays
* - Date objects
* - RegExp objects
* - Map and Set
* - TypedArrays (e.g. Uint8Array, Float32Array, etc.)
* - DataView
* - Plain objects (preserving prototype)
*
* This function **does not** perform deep cloning.
*
* @template T - The type of the value to clone
* @param {T} value - The value to be cloned
* @returns {T} - A shallow copy of the original value
*
* @example
* // Clone an array
* const arr = [1, 2, 3];
* const clonedArr = clone(arr);
* console.log(clonedArr); // [1, 2, 3]
* console.log(clonedArr === arr); // ❌ false
*
* @example
* // Clone an object
* const obj = { a: 1, b: { c: 2 } };
* const clonedObj = clone(obj);
* console.log(clonedObj); // { a: 1, b: { c: 2 } }
* console.log(clonedObj === obj); // ❌ false
* console.log(clonedObj.b === obj.b); // ✅ true (shallow clone)
*
* @example
* // Clone a Date
* const date = new Date();
* const clonedDate = clone(date);
* console.log(clonedDate); // Same date as original
* console.log(clonedDate === date); // ❌ false
*
* @example
* // Clone a Map
* const map = new Map([['key', 'value']]);
* const clonedMap = clone(map);
* console.log(clonedMap.get('key')); // 'value'
* console.log(clonedMap === map); // ❌ false
*/
declare function clone<T>(value: T): T;
/**
* Performs a shallow clone of `value`, invoking `customizer` to customize the cloned value.
* If `customizer` returns a defined value, it is used instead of the default clone.
*
* @template T
* @param value - The value to clone.
* @param customizer - Optional customizer invoked with the value.
* @returns {T} The cloned value.
*
* @example
* const obj = { a: 1, b: 2 };
* const result = cloneWith(obj, (val) => typeof val === 'number' ? val + 1 : undefined);
* // result => { a: 2, b: 3 }
*/
declare function cloneWith<T>(value: T, customizer: Customizer<T>): T;
type Primitive = string | number | boolean | bigint | symbol | null | undefined;
/**
* Determines if a type is a Tuple (i.e., fixed-length array)
* Tuples have known numeric literal keys and `length` is a constant number.
*/
type IsTuple<T> = T extends readonly any[] ? number extends T['length'] ? false : true : false;
/**
* Recursive deep copy type utility.
* - Preserves tuples and arrays
* - Removes `readonly` from properties
* - Recursively copies object properties, including symbol keys
*/
type CloneDeep<T> = T extends Primitive | ((...args: any[]) => any) ? T : IsTuple<T> extends true ? {
[K in keyof T]: CloneDeep<T[K]>;
} : T extends ReadonlyArray<infer U> ? Array<CloneDeep<U>> : T extends Map<infer K, infer V> ? Map<K, CloneDeep<V>> : T extends Set<infer U> ? Set<CloneDeep<U>> : T extends Date ? Date : T extends RegExp ? RegExp : T extends object ? {
-readonly [K in keyof T]: CloneDeep<T[K]>;
} & {
[K in keyof T as K extends symbol ? K : never]: CloneDeep<T[K]>;
} : T;
/**
* @description Customizer function type for cloneDeepWith
* If it returns `undefined`, cloneDeepWith falls back to default deep cloning logic
*/
type CloneDeepCustomizer = (value: any, key?: PropertyKey, object?: any) => any;
/**
* @description Deep copy an object.
* Unsupported types like functions, symbols, WeakMap, WeakSet will trigger a warning and return original.
* @param {any} obj - The object to deep copy
* @param {WeakMap<object, any>} [seen=new WeakMap()] - WeakMap cache to track copied references
* @returns {any} - Deep copied object
*
* @example
* const original = { name: "Alice", details: { age: 25 } };
* const copy = cloneDeep(original);
* console.log(copy.details.age); // ✅ 25
* console.log(copy !== original); // ✅ true
*
* @example
* const arr = [1, { nested: true }, [3]];
* const copy = cloneDeep(arr);
* console.log(copy[1].nested); // ✅ true
*
* @example
* const map = new Map<string, number>([["a", 1], ["b", 2]]);
* const copy = cloneDeep(map);
* console.log(copy.get("b")); // ✅ 2
*
* @example
* const set = new Set([1, 2, 3]);
* const copy = cloneDeep(set);
* console.log(copy.has(2)); // ✅ true
*
* @example
* const date = new Date("2020-01-01");
* const copy = cloneDeep(date);
* console.log(copy.getFullYear()); // ✅ 2020
*
* @example
* const regex = /abc/gi;
* const copy = cloneDeep(regex);
* console.log(copy.source); // ✅ "abc"
* console.log(copy.flags); // ✅ "gi"
*
* @example
* const obj: any = {};
* obj.self = obj;
* const copy = cloneDeep(obj);
* console.log(copy.self === copy); // ✅ true
*/
declare function cloneDeep<T>(obj: T, seen?: WeakMap<object, any>): CloneDeep<T>;
/**
* @description Deep copy with customizer support.
* @param {any} obj - The object to clone
* @param {CloneDeepCustomizer} customizer - Custom clone handler, can override cloning behavior
* @param {WeakMap<object, any>} [seen=new WeakMap()] - Circular reference cache
* @returns {any} - Cloned object with possible customization
*
* @example
* const original = { x: new Date(), y: 123 };
* const copy = cloneDeepWith(original, (val) => {
* if (val instanceof Date) return 'DATE';
* });
* console.log(copy.x); // ✅ "DATE"
*/
declare function cloneDeepWith<T>(obj: T, customizer: CloneDeepCustomizer, seen?: WeakMap<object, any>): CloneDeep<T>;
/**
* Checks if a value exists in an array starting from a given index.
*
* @template T
* @param {T[]} source - The array to search.
* @param {T} target - The value to search for.
* @param {number} [fromIndex=0] - The index to start searching from.
* @returns {boolean} True if found, otherwise false.
* @example
* includes([1, 2, 3], 2); // ✅ true
* includes([1, 2, 3], 2, 2); // ❌ false
*/
declare function includes<T>(source: T[], target: T, fromIndex?: number): boolean;
/**
* Checks if a substring exists in a string starting from a given index.
*
* @param {string} source - The string to search.
* @param {string} target - The substring to search for.
* @param {number} [fromIndex=0] - The index to start searching from.
* @returns {boolean} True if found, otherwise false.
* @example
* includes('hello world', 'world'); // ✅ true
* includes('hello world', 'world', 8); // ❌ false
*/
declare function includes(source: string, target: string, fromIndex?: number): boolean;
/**
* Checks if a value exists among the values of an object.
*
* @param {Record<string, any>} source - The object to search.
* @param {*} target - The value to search for.
* @returns {boolean} True if found, otherwise false.
* @example
* includes({ a: 1, b: 2 }, 2); // ✅ true
*/
declare function includes(source: Record<string, any>, target: any): boolean;
/**
* Checks if a key exists in a Map.
*
* @template K, V
* @param {Map<K, V>} source - The Map to search.
* @param {K} target - The key to search for.
* @returns {boolean} True if found, otherwise false.
* @example
* includes(new Map([['a', 1], ['b', 2]]), 'b'); // ✅ true
*/
declare function includes<K, V>(source: Map<K, V>, target: K): boolean;
/**
* Checks if a value exists in a Set.
*
* @template T
* @param {Set<T>} source - The Set to search.
* @param {T} target - The value to search for.
* @returns {boolean} True if found, otherwise false.
* @example
* includes(new Set([1, 2, 3]), 2); // ✅ true
*/
declare function includes<T>(source: Set<T>, target: T): boolean;
export { clone, cloneDeep, cloneDeepWith, cloneWith, includes };