UNPKG

@rzl-zone/utils-js

Version:

A modern, lightweight set of JavaScript utility functions with TypeScript support for everyday development, crafted to enhance code readability and maintainability.

1,111 lines (1,084 loc) 107 kB
/*! * ==================================================== * Rzl Utils-JS. * ---------------------------------------------------- * Version: 3.11.0. * Author: Rizalvin Dwiky. * Repository: https://github.com/rzl-zone/utils-js. * ==================================================== */ import { NumberRangeUnion, Prettify, IsPositive, ParseNumber, Extends, IsStringLiteral, CharAt, AnyString, AnyFunction, IsEmptyString, Trim, IsAny, AnObjectNonArray, IsArray, OrArr, IsNever, TypedArray } from '@rzl-zone/ts-types-plus'; import { a as IsPlainObjectResult, c as IsHasKeysObject } from '../isPlainObject-0p3VveWr.js'; export { A as AcronymsList, G as GetPreciseTypeOptions, I as IsNumberOptions, g as getPreciseType, i as isNumber, b as isPlainObject } from '../isPlainObject-0p3VveWr.js'; /** ---------------------------------------------------------- * * ***Predicate: `areArraysEqual`.*** * ---------------------------------------------------------- * **Compares two arrays deeply to check if they are equal.** * @description Supports deep comparison of arrays containing nested arrays or objects, * can also ignore the order of elements at all levels by recursively sorting. * @param {unknown[]} array1 * ***The first array to compare, can contain nested arrays or objects.*** * @param {unknown[]} array2 * ***The second array to compare against, should match structure of `array1`.*** * @param {boolean|undefined} [ignoreOrder=false] * ***Whether to ignore the order of elements when comparing.*** * - If `true`, will sort both arrays recursively before comparing, default is `false`. * @returns {boolean} * Returns `true` if both arrays are deeply equal, otherwise `false`. * @throws **{@link TypeError | `TypeError`}** if `array1` or `array2` are not arrays, or if `ignoreOrder` is not a boolean. * @example * ```ts * areArraysEqual([1, 2, 3], [1, 2, 3]); * // ➔ true * areArraysEqual([1, 2, 3], [3, 2, 1]); * // ➔ false * areArraysEqual([1, 2, 3], [3, 2, 1], true); * // ➔ true (order ignored) * areArraysEqual([{ x: 1 }, { y: 2 }], [{ y: 2 }, { x: 1 }], true); * // ➔ true * ``` */ declare const areArraysEqual: (array1: unknown[], array2: unknown[], ignoreOrder?: boolean) => boolean; /** --------------------------------- * * ***Predicate: `areObjectsEqual`.*** * --------------------------------- * **Compares two objects for deep equality.** * @template T1 The type of the first object. * @template T2 The type of the second object. * @param {*} object1 - The first object to compare. * @param {*} object2 - The second object to compare. * @returns {boolean} Return `true` if both objects are deeply equal, otherwise `false`. * @example * areObjectsEqual({ a: 1, b: 2 }, { a: 1, b: 2 }); * // ➔ true * areObjectsEqual({ a: 1 }, { a: 1, b: undefined }); * // ➔ false * areObjectsEqual([1, 2, 3], [1, 2, 3]); * // ➔ true */ declare const areObjectsEqual: (object1: unknown, object2: unknown) => boolean; /** --------------------------------- * * ***Predicate: `areURLsEqualPath`.*** * --------------------------------- * **Checks if two URLs are the same, ignoring query parameters, this function compares only the protocol, host, and pathname.** * @param {URL} urlA - The first URL to compare. * @param {URL} urlB - The second URL to compare. * @returns {boolean} Returns `true` if both URLs are the same (ignoring search parameters), otherwise `false`. * @example * // Same domain, same path, different query -> true * areURLsEqualPath( * new URL("https://example.com/page?a=1"), * new URL("https://example.com/page?b=2") * ); * // ➔ true * * // Same domain, different path -> false * areURLsEqualPath( * new URL("https://example.com/page1"), * new URL("https://example.com/page2") * ); * // ➔ false * * // Different protocol -> false * areURLsEqualPath( * new URL("http://example.com/page"), * new URL("https://example.com/page") * ); * // ➔ false * * // Same protocol, same host, same path (ignores query & hash) -> true * areURLsEqualPath( * new URL("https://example.com/page#section"), * new URL("https://example.com/page") * ); * // ➔ true */ declare const areURLsEqualPath: (urlA: URL, urlB: URL) => boolean; /** --------------------------------- * * ***Predicate: `areURLsIdentical`.*** * --------------------------------- * **Checks if two URLs are exactly the same, including protocol, host, pathname, and query parameters.** * @param {URL} urlA - The first URL to compare. * @param {URL} urlB - The second URL to compare. * @returns {boolean} Returns `true` if both URLs are identical, otherwise `false`. * @example * // Identical URLs -> true * areURLsIdentical( * new URL("https://example.com/page?a=1"), * new URL("https://example.com/page?a=1") * ); * // ➔ true * * // Same path, different query parameter -> false * areURLsIdentical( * new URL("https://example.com/page?a=1"), * new URL("https://example.com/page?b=2") * ); * // ➔ false * * // Same host & query, but different protocol -> false * areURLsIdentical( * new URL("http://example.com/page?a=1"), * new URL("https://example.com/page?a=1") * ); * // ➔ false * * // Same everything except trailing slash -> false * areURLsIdentical( * new URL("https://example.com/page"), * new URL("https://example.com/page/") * ); * // ➔ false */ declare const areURLsIdentical: (urlA: URL, urlB: URL) => boolean; type OptionsTextContainsAll = { /** If `true`, matches whole words only, defaultValue is `false`. * * @default false */ exactMatch?: boolean; /** Optional regex flags (default: `"i"` for case-insensitive). * * @default "i" */ flags?: string; }; /** ---------------------------------------------------------- * * ***Predicate: `textContainsAll`.*** * ---------------------------------------------------------- * **Checks if the given `text` contains all of the specified `searchWords`.** * - **Behavior:** * - Returns `false` if `text` or `searchWords` is `null`/`undefined`/invalid. * - Uses **regular expressions** for flexible pattern matching. * - **Escapes special characters** to prevent regex injection attacks. * - **Trims input** to avoid false positives with empty spaces. * - **Supports exact word matching** (optional). * @param {string|null|undefined} text - The string text to search within. * @param {string[]|null} [searchWords] - An array of words/phrases to match against the text. * @param {OptionsTextContainsAll} [options] - Optional configuration object. * @param {OptionsTextContainsAll["exactMatch"]} [options.exactMatch=false] - If `true`, matches whole words only, defaultValue is `false`. * @param {OptionsTextContainsAll["flags"]} [options.flags="i"] - Optional regex flags (default: `"i"` for case-insensitive). * @returns {boolean} Return `true` if all `searchWords` are found in `text`, otherwise `false`. * @example * textContainsAll("Hello world, WithAI APP", ["Hello", "world"]); * // ➔ true * textContainsAll("JavaScript and TypeScript", ["Java", "Script"]); * // ➔ true * textContainsAll("Machine Learning", ["AI", "Learning"]); * // ➔ false * textContainsAll("open-source", ["open"], { exactMatch: true }); * // ➔ false (because options `exactMatch=true`) * textContainsAll(null, ["test"]); * // ➔ false (invalid text) * textContainsAll("Hello", null); * // ➔ false (invalid searchWords) */ declare const textContainsAll: <T extends string>(text?: T | null, searchWords?: T[] | string[] | null, options?: OptionsTextContainsAll) => boolean; type OptionsTextContainsAny = { /** If `true`, matches whole words only, defaultValue is `false`. * * @default false */ exactMatch?: boolean; /** Optional regex flags (default: `"i"` for case-insensitive). * * @default "i" */ flags?: string; }; /** ---------------------------------------------------------- * * ***Predicate: `textContainsAny`.*** * ---------------------------------------------------------- * **Checks if the given `text` contains at least one of the specified `searchWords`.** * - **Behavior:** * - Returns `false` if `text` or `searchWords` is `null`/`undefined`/invalid. * - Uses **regular expressions** for flexible pattern matching. * - **Escapes special characters** to prevent regex injection attacks. * - **Trims input** to avoid false positives with empty spaces. * - **Supports exact word matching** (optional). * @param {string|null|undefined} text - The string text to search within. * @param {string[]|null} [searchWords] - An array of words/phrases to match against the text. * @param {OptionsTextContainsAny} [options] - Optional configuration object. * @param {OptionsTextContainsAny["exactMatch"]} [options.exactMatch=false] - If `true`, matches whole words only, defaultValue is `false`. * @param {OptionsTextContainsAny["flags"]} [options.flags="i"] - Optional regex flags (default: `"i"` for case-insensitive). * @returns {boolean} Return `true` if at least one `searchWord` is found in `text`, otherwise `false`. * @example * textContainsAny("Hello world", ["hello", "test"]); * // ➔ true * textContainsAny("withAI APP", ["chat", "ai"]); * // ➔ false * textContainsAny("TypeScript is great!", ["script", "java"]); * // ➔ true * textContainsAny("open-source", ["open"], { exactMatch: true }); * // ➔ false (because options `exactMatch=true`) * textContainsAny(null, ["test"]); * // ➔ false (invalid text) * textContainsAny("Hello", null); * // ➔ false (invalid searchWords) */ declare const textContainsAny: <T extends string>(text?: T | null, searchWords?: T[] | string[] | null, options?: OptionsTextContainsAny) => boolean; /** ---------------------------------------------------------- * * ***Predicate: `doesKeyExist`.*** * ---------------------------------------------------------- * **Recursively checks if a given key exists in an object or array.** * - **Behavior:** * - **Supports deeply nested objects and arrays**, searching recursively. * - Uses `Object.prototype.hasOwnProperty.call()` to safely check if the * key exists at each level, even if its value is `null` or `undefined`. * - Optimized to return `true` immediately when the key is found (short-circuits). * - Handles edge cases gracefully: * - Returns `false` for `null`, `undefined`, or non-object inputs. * - Returns `false` if key is not found anywhere, even in deeply nested * structures. * - **ℹ️ Note:** * - This function only checks for **the existence of the key itself**, * not whether its value is non-null or non-undefined. * - If you need to check for both existence and meaningful value, write a stricter function. * @template T - The type of the input object or array. * @param {T | Record<string, unknown> | unknown[]} object - The object or array to search. * @param {PropertyKey} key - The key to look for (string, number, or symbol). * @returns {boolean} Returns `true` if the key exists anywhere in the object or array (even with `null` / `undefined` value), otherwise `false`. * @example * doesKeyExist({ name: "John", age: 30 }, "age"); * // ➔ true * doesKeyExist({ user: { profile: { email: "test@example.com" } } }, "email"); * // ➔ true * doesKeyExist([{ id: 1 }, { id: 2 }], "id"); * // ➔ true * doesKeyExist({ a: { b: { c: 10 } } }, "d"); * // ➔ false * doesKeyExist(null, "name"); * // ➔ false * doesKeyExist(undefined, "test"); * // ➔ false * * // Key exists even if value is null or undefined: * doesKeyExist({ a: null, b: undefined, c: { d: null } }, "a"); // ➔ true * doesKeyExist({ a: null, b: undefined, c: { d: null } }, "b"); // ➔ true * doesKeyExist({ a: null, b: undefined, c: { d: null } }, "d"); // ➔ true * * doesKeyExist({ a: 1 }, true); * // ➔ ❌ Throws TypeError * doesKeyExist({ a: 1 }, ["not", "valid"]); * // ➔ ❌ Throws TypeError */ declare const doesKeyExist: (object: Record<string, unknown> | unknown[], key: PropertyKey) => boolean; /** ---------------------------------------------------------- * * ***Predicate: `arrayHasAnyMatch`.*** * ---------------------------------------------------------- * **Checks if at least one element from `targetArray` exists in `sourceArray`.** * - **Behavior:** * - Uses `Set` for **faster lookup** compared to `Array.prototype.includes()`. * - Supports **any data type** (`number`, `string`, `boolean`, `object`, `array`, `function`, etc.). * - Uses **reference equality** for non-primitive values (object, array, function). * - Returns `false` if either array is missing, empty, or not an array. * @template T - The expected type of array elements. * @param {T[] | null | undefined} sourceArray - The array to search within. * @param {T[] | null | undefined} targetArray - The array containing elements to match. * @returns {boolean} * ***Return:*** * - `true` if **at least one element from `targetArray` is strictly found * in `sourceArray`**. * - Comparison uses: * - **Value equality** for primitives (`number`, `string`, `boolean`, `null`, `undefined`). * - **Reference equality** for `objects`, `arrays`, and `functions`. * - `false` if: * - No matching elements exist, * - Either array is not provided, not an actual array, or is empty. * @example * arrayHasAnyMatch(["apple", "banana", "cherry"], ["banana", "grape"]); * // ➔ true * arrayHasAnyMatch(["red", "blue"], ["green", "yellow"]); * // ➔ false * arrayHasAnyMatch([1, 2, 3], [3, 4, 5]); * // ➔ true * arrayHasAnyMatch([], ["test"]); * // ➔ false * arrayHasAnyMatch(["A", "B", "C"], []); * // ➔ false * * const obj = { x: 1 }; * arrayHasAnyMatch([obj], [obj]); * // ➔ true (same reference) * arrayHasAnyMatch([{ x: 1 }], [{ x: 1 }]); * // ➔ false (different reference) * * const fn = () => "hello"; * arrayHasAnyMatch([fn], [fn]); * // ➔ true * arrayHasAnyMatch([() => "hello"], [() => "hello"]); * // ➔ false (different function reference) * * const arr = [1, 2]; * arrayHasAnyMatch([arr], [arr]); * // ➔ true * arrayHasAnyMatch([[1, 2]], [[1, 2]]); * // ➔ false (different array object) */ declare const arrayHasAnyMatch: <T>(sourceArray: T[] | null | undefined, targetArray: T[] | null | undefined) => boolean; /** Restrict array indices to a fixed numeric range (1–25). */ type ArrayIndex = NumberRangeUnion<1, 25>; /** Remove `undefined` from a type. */ type NonUndef<T> = T extends undefined ? never : T; /** Remove `null` from a type. */ type NonNull<T> = T extends null ? never : T; /** Convert optional boolean for "discard undefined" to actual boolean. */ type EffectiveDiscardUndefined<O extends boolean | undefined> = O extends boolean ? O : true; /** Convert optional boolean for "discard null" to actual boolean. */ type EffectiveDiscardNull<O extends boolean | undefined> = O extends boolean ? O : false; /** Unwrap array type. */ type UnwrapArray<T> = T extends (infer U)[] ? U : T extends readonly (infer U)[] ? U : T; /** Force symbol key to be deep required. */ type IsOptionalKey<T, K extends keyof T> = Record<never, never> extends Pick<T, K> ? true : false; /** * ***Returns numeric keys of an object.*** * * @private ***types for {@link hasOwnProp}.*** */ type NumericKeyOfHasOwnProp<Obj> = Extract<keyof Obj, number>; /** * ***Generate all nested keys of an object or array in dot/bracket notation.*** * * @private ***types for {@link hasOwnProp}.*** * * Example: * ```ts * type Keys = NestedKeyOfHasOwnProp<{ users: { name: string }[] }> * // Keys = "users" | "users.[number]" | "users.[number].name" * ``` */ type NestedKeyOfHasOwnProp<T> = T extends readonly (infer U)[] ? `[${number}]` | `[${number}].${NestedKeyOfHasOwnProp<U>}` : T extends object ? { [K in keyof T & (string | number)]: K extends string | number ? NonNullable<T[K]> extends readonly unknown[] ? `${K}` | `${K}.[${ArrayIndex}]` | `${K}.[${ArrayIndex}].${NestedKeyOfHasOwnProp<UnwrapArray<T[K]>>}` : NonNullable<T[K]> extends object ? `${K}` | `${K}.${NestedKeyOfHasOwnProp<NonNullable<T[K]>>}` : `${K}` : never; }[keyof T & (string | number)] : never; /** Apply discard rules to the last key of a path. * * Rules: * - If discardUndefined=true -> remove `undefined` from value * - If discardNull=true -> remove `null` from value * * Order: first strip undefined (if requested), then strip null (if requested) */ type ApplyLastRulesHasOwnProp<V, DiscardU extends boolean, DiscardN extends boolean> = DiscardU extends true ? DiscardN extends true ? NonNull<NonUndef<V>> : NonUndef<V> : DiscardN extends true ? NonNull<V> : V | Extract<V, undefined>; /** Force an array index N to type U. */ type RefineArrayAtIndex<T extends readonly unknown[], N extends number, U> = T & { [K in N]: U; }; /** Narrow object/array type based on a path string. * * @template T - object type to narrow * @template P - path string like "user.addresses.[2].zip" * @template DU - discard undefined * @template DN - discard null */ type NarrowByPathHasOwnProp<T, P extends string, DU extends boolean = true, DN extends boolean = false> = P extends `${infer Head}.${infer Rest}` ? Head extends `[${infer N extends number}]` ? T extends readonly (infer U)[] ? RefineArrayAtIndex<T, N, NarrowByPathHasOwnProp<U, Rest, DU, DN>> : T : Head extends keyof T ? Rest extends `[${infer M extends number}]${infer R}` ? M extends R ? { [K in keyof T]-?: NarrowByPathHasOwnProp<EffectiveDiscardUndefined<DU> extends true ? NonUndef<T[K]> : EffectiveDiscardNull<DN> extends true ? NonNull<T[K]> : T[K], Rest, DU, DN>; } : EffectiveDiscardUndefined<DU> extends true ? { [K in keyof T]-?: K extends Head ? Exclude<NarrowByPathHasOwnProp<EffectiveDiscardNull<DN> extends true ? Exclude<T[K], null> : EffectiveDiscardUndefined<DU> extends true ? Exclude<T[K], undefined> : T[K], Rest, DU, DN>, undefined> : EffectiveDiscardNull<DN> extends true ? Exclude<T[K], null> : EffectiveDiscardUndefined<DU> extends true ? Exclude<T[K], undefined> : T[K]; } : { [K in keyof T]: K extends Head ? NarrowByPathHasOwnProp<EffectiveDiscardNull<DN> extends true ? Exclude<T[K], null> : EffectiveDiscardUndefined<DU> extends true ? Exclude<T[K], undefined> : T[K], Rest, DU, DN> : EffectiveDiscardNull<DN> extends true ? Exclude<T[K], null> : EffectiveDiscardUndefined<DU> extends true ? Exclude<T[K], undefined> : T[K]; } : { [K in keyof T]: K extends Head ? NarrowByPathHasOwnProp<NonNullable<T[K]>, Rest, DU, DN> : T[K]; } & { [K in Head]-?: NarrowByPathHasOwnProp<NonNullable<T[K]>, Rest, DU, DN>; } : T : P extends `[${infer N extends number}]` ? T extends readonly (infer U)[] ? RefineArrayAtIndex<T, N, ApplyLastRulesHasOwnProp<NonNullable<U>, DU, DN>> : T : P extends keyof T ? DU extends true ? { [K in keyof T]: K extends P ? ApplyLastRulesHasOwnProp<T[K], DU, DN> : T[K]; } & { [K in P]-?: ApplyLastRulesHasOwnProp<T[P], DU, DN>; } : { [K in keyof T]: K extends P ? ApplyLastRulesHasOwnProp<T[K], DU, DN> : T[K]; } : T; /** * ***Expand an array/string/function into a nested type according * to a dot/bracket path.*** * @private ***types for {@link hasOwnProp}.*** */ type SmartDetectStringHasOwnProp<Obj extends string | undefined | null, Key extends string | number> = Obj extends undefined ? undefined : Obj extends null ? null : IsPositive<ParseNumber<Key>> extends true ? Extends<IsStringLiteral<Obj>, true> extends true ? CharAt<Exclude<Obj, null | undefined>, ParseNumber<Key>> : string | undefined | null : IsPositive<ParseNumber<Key>> extends true ? string | undefined | null : AnyString | undefined | null; /** @private ***types for {@link hasOwnProp}.*** */ type SmartDetectArrayFuncHasOwnProp<Obj extends unknown[] | AnyFunction, Key extends PropertyKey> = Prettify<Obj & DotToNestedSpecialSmartDetect<Key> & { length: number; }, { recursive: false; }>; /** * ***Smartly detect nested path keys of an unknown object or function, * falls-back to inferred nested structure when path is not valid.*** * * @private ***types for {@link hasOwnProp}.*** */ type SmartDetectUnknownKeyHasOwnProp<Obj extends unknown | AnyFunction, Key extends PropertyKey, DiscardUndefined extends boolean = true, DiscardNull extends boolean = false> = Trim<Key> extends "" ? Obj : Prettify<Obj & (Key extends NestedKeyOfHasOwnProp<Obj> ? GuardedHasOwnProp<Obj, Key, DiscardUndefined, DiscardNull> : DotToNestedSpecialSmartDetect<Key>), { recursive: true; }>; /** Convert dot/bracket path string to nested object type with leaf value. * Path not found in object key ➔ return unknown. */ type DotToNestedSpecialSmartDetect<Path extends PropertyKey, Value = unknown> = IsEmptyString<Extract<Path, string>> extends true ? undefined : Path extends `${infer Head}.${infer Rest}` ? Head extends `[${number}]` ? DotToNestedSpecialSmartDetect<Rest, Value>[] : { [Key in Head]: DotToNestedSpecialSmartDetect<Rest, Value>; } : Path extends `[${number}]` ? Value[] : { [Key in Path]: Value; }; /** * ***Guarded wrapper for `NarrowByPathHasOwnProp` with `Prettify`.*** * @private ***types for {@link hasOwnProp}.*** */ type GuardedHasOwnProp<Obj, Key extends NestedKeyOfHasOwnProp<Obj>, DiscardUndefined extends boolean | undefined, DiscardNull extends boolean | undefined> = Prettify<Obj & NarrowByPathHasOwnProp<Obj, Key & string, EffectiveDiscardUndefined<DiscardUndefined>, EffectiveDiscardNull<DiscardNull>>, { recursive: true; }>; /** * ***Make a specific symbol key deeply required in an object symbols.*** * **Used internally to enforce stronger type narrowing.** * @private ***types for {@link hasOwnProp}.*** */ type DeepRequiredSymbolHasOwnProp<Obj, Sym extends symbol, DU extends boolean = true, DN extends boolean = false> = Prettify<Obj & ({ [K in keyof Obj & Sym as DU extends true ? K : never]-?: DN extends true ? NonNull<NonUndef<Obj[K]>> : NonUndef<Obj[K]>; } & { [K in keyof Obj & Sym as DU extends true ? never : K]?: DN extends true ? NonNull<Obj[K]> : Obj[K]; }), { recursive: true; }>; /** * ***Apply discard rules to numeric keys in an object type.*** * * - If `discardUndefined = true` ➔ undefined removed, key required * - If `discardNull = true` ➔ null removed * * @private ***types for {@link hasOwnProp}.*** */ type NumericKeyHasOwnPropMapped<Obj extends object, K extends NumericKeyOfHasOwnProp<Obj>, DU extends boolean, DN extends boolean> = Prettify<Obj & (IsOptionalKey<Obj, K> extends true ? { [P in K]?: DN extends true ? NonNull<Obj[K]> : Obj[K]; } & (DU extends true ? { [P in K]-?: NonUndef<Obj[K]>; } : Record<never, never>) : { [P in K]-?: DN extends true ? NonNull<Obj[K]> : Obj[K]; } & (DU extends true ? { [P in K]-?: NonUndef<Obj[K]>; } : Record<never, never>)), { recursive: true; }>; /** * ***Options to control `hasOwnProp` behavior.*** * @private ***types options for {@link hasOwnProp}.*** */ type HasOwnPropOptions<DiscardUndefined extends boolean = true, DiscardNull extends boolean = false> = { /** If `true` ***(default)***, properties with `undefined` values are treated as non-existent. * * - **Effects:** * - **Runtime:** `hasOwnProp(obj, key)` returns `false` if the property exists but its value is `undefined`. * - **TypeScript narrowing:** The property's type is narrowed to exclude `undefined`. * - **Example:** * ```ts * const obj = { a: undefined, b: 123 }; * hasOwnProp(obj, "a"); // ➔ false * hasOwnProp(obj, "a", { discardUndefined: false }); // ➔ true * ``` */ discardUndefined?: DiscardUndefined; /** If `true` ***(default: `false`)***, properties with `null` values are treated as non-existent. * * - **Effects:** * - **Runtime:** `hasOwnProp(obj, key)` returns `false` if the property exists but its value is `null`. * - **TypeScript narrowing:** The property's type is narrowed to exclude `null`. * - **Example:** * ```ts * const obj = { a: null, b: 123 }; * hasOwnProp(obj, "a"); // ➔ true (default discardNull = false) * hasOwnProp(obj, "a", { discardNull: true }); // ➔ false * ``` */ discardNull?: DiscardNull; }; /** ------------------------------------------------------- * * ***Predicate: `hasOwnProp`.*** * ------------------------------------------------------- * **A **type-safe** replacement for `Object.prototype.hasOwnProperty` with runtime validation and **TypeScript-aware type narrowing**.** * - #### Supported Targets: * - **Plain objects** ➔ `{ foo: "bar" }`. * - **Arrays** ➔ `[ { id: 1 }, { id: 2 } ]`. * - **Strings** ➔ `"hello"` (as array-like objects with `.length`, index, etc.). * - **Functions** ➔ callable objects with extra props. * - **Symbols** ➔ own property symbols. * - #### Key Advantages over `in` or `obj.hasOwnProperty(key)`: * - Supports **dot/bracket path notation** (e.g. `"user.address.city"`, `"addresses[0].zip"`). * - Handles **symbol** keys safely. * - **Narrows** the type of `obj` in TypeScript (stronger type safety). * - Configurable handling of **`undefined`** and **`null`**. * - #### Runtime Behavior: * - ***✅ Returns `true` if:*** * - Value `obj` is an object/array/string/function **and** the property * exists **and**, it passes the `options` checks. * - ***❌ Returns `false` if:*** * - Value `obj` is not a valid type. * - The property does not exist. * - The value is `undefined` and `discardUndefined: true` (**default**). * - The value is `null` and `discardNull: true`. * - The `key` (after trimming) is an **empty string** ➔ treated as **invalid**. * - #### TypeScript Behavior: * - ***Inside an `if (hasOwnProp(...)) {}` block:*** * - The property is **guaranteed to exist**. * - Depending on `options`, the property type is narrowed to exclude * `undefined` and/or `null`. * - #### ⚠️ Caveats: * - ***Empty keys are invalid:*** * - If the `key` string is empty (`""`) after trimming whitespace or other characters, * it will **not** be considered a valid property and always returns `false`. * - ***Arrays are limited by TypeScript inference:*** * - Checking index `[0]` only narrows **that specific index**, not the rest, example: * 1. `hasOwnProp(users, "[0].id")` does **not** imply `users[1].id` exists. * - 👉 For different indices, use **optional chaining** (`users[1]?.id`). * - ***Autocomplete limitation for array indices:*** * - Autocompletion for `[index]` is only supported up to **25** (`[0]` ➔ `[24]`). * - This limit is intentional for **performance and safety:** * 1. Generating infinite union types for all possible indices would cause * **TypeScript IntelliSense to hang or crash**. * - ℹ️ You can still check higher indices manually (e.g. `"[999].id"`), * but they will not show up in IntelliSense suggestions. * @param {HasOwnPropOptions} [options] - ***Optional configuration object.*** * @param {HasOwnPropOptions["discardUndefined"]} [options.discardUndefined=true] * ***If `true`, properties with `undefined` values are treated as **missing**, default: `true`.*** * @param {HasOwnPropOptions["discardNull"]} [options.discardNull=false] * ***If `true`, properties with `null` values are treated as **missing**, default: `false`.*** * @param {*} obj ***The `object`, `array`, `string`, `function`, or `other value` to check against.*** * @param {PropertyKey} key * ***The property key to check, can be:*** * - `string` (supports dot/bracket paths, e.g. `"user.address.city"`, `"[0].id"`). * - `number` (array-like index). * - `symbol` (own property symbols). * @returns {boolean} Return `true` if the property exists (and passes `options`), otherwise `false`. * @example * * - #### ✅ Objects: * ```ts * const obj: { name?: string | null } = {}; * * if (hasOwnProp(obj, "name")) { * // obj is now ➔ { name: string | null } * console.log(obj.name); // string | null * } * * if (hasOwnProp(obj, "name", { discardUndefined: true, discardNull: true })) { * // obj is now ➔ { name: string } * console.log(obj.name.toUpperCase()); // safe * } * ``` * - #### ✅ Arrays: * ```ts * const users = [{ id: 1 }, { id: 2 }]; * * if (hasOwnProp(users, "[1].id")) { * // ➔ users[1].id is guaranteed to exist * console.log(users[1].id); // number * } * * // ⚠️ Caveat: narrowing only applies to checked index * if (hasOwnProp(users, "[0].id")) { * console.log(users[0].id); // ✅ safe * console.log(users[1].id); // ❌ not guaranteed! * } * * // 👉 Solution: optional chaining * console.log(users[1]?.id); // ➔ safe, even without narrowing * ``` * * - #### ✅ Symbols: * ```ts * const secret = Symbol("secret"); * const obj2 = { [secret]: 42 }; * * if (hasOwnProp(obj2, secret)) { * console.log(obj2[secret] + 1); // ➔ 43 * } * ``` * - #### ✅ Strings: * ```ts * if (hasOwnProp("hello", "length")) { * console.log("hello".length); // ➔ 5 * } * * if (hasOwnProp("hello", 1)) { * console.log("hello"[1]); // ➔ "e" * } * ``` * - #### ✅ Functions: * ```ts * function fn() {} * fn.extra = 123; * * if (hasOwnProp(fn, "extra")) { * console.log(fn.extra); // ➔ 123 * } * ``` * - #### ❌ Empty key: * ```ts * const obj = { a: 1 }; * * hasOwnProp(obj, ""); // ➔ false (invalid key) * hasOwnProp(obj, " "); // ➔ false (trimmed to empty) * ``` */ declare function hasOwnProp<Obj>(obj: IsAny<Obj> extends true ? Obj : never, key: PropertyKey, options?: HasOwnPropOptions<boolean, boolean> /** @ts-expect-error we force `any` to `unknown` at result */ ): obj is unknown; declare function hasOwnProp<Obj extends null | undefined>(obj: Obj, key: PropertyKey, options?: HasOwnPropOptions<boolean, boolean>): false; declare function hasOwnProp<Obj extends object | AnyFunction, Key extends NestedKeyOfHasOwnProp<Obj>, DiscardUndefined extends boolean = true, DiscardNull extends boolean = false>(obj: Obj | null | undefined, key: Key, options?: HasOwnPropOptions<DiscardUndefined, DiscardNull> /** @ts-expect-error we force to override recursive type result */ ): obj is GuardedHasOwnProp<Obj, Key, DiscardUndefined, DiscardNull>; declare function hasOwnProp<Obj extends object, Num extends NumericKeyOfHasOwnProp<Obj>, DiscardUndefined extends boolean = true, DiscardNull extends boolean = false>(obj: Obj | null | undefined, key: Num, options?: HasOwnPropOptions<DiscardUndefined, DiscardNull> /** @ts-expect-error we force to override recursive type result */ ): obj is NumericKeyHasOwnPropMapped<Obj, Num, DiscardUndefined, DiscardNull>; declare function hasOwnProp<Obj extends object | AnyFunction, Sym extends symbol, DiscardUndefined extends boolean = true, DiscardNull extends boolean = false>(obj: Obj | null | undefined, key: Sym, options?: HasOwnPropOptions<DiscardUndefined, DiscardNull> /** @ts-expect-error we force to override recursive type result */ ): obj is DeepRequiredSymbolHasOwnProp<Obj, Sym, DiscardUndefined, DiscardNull>; declare function hasOwnProp<Obj extends string | null | undefined, Key extends string | number>(obj: Obj | null | undefined, key: Key, options?: HasOwnPropOptions<boolean, boolean> /** @ts-expect-error we force to override recursive type result */ ): obj is IsStringLiteral<SmartDetectStringHasOwnProp<Obj, Key>> extends true ? AnyString | SmartDetectStringHasOwnProp<Obj, Key> : SmartDetectStringHasOwnProp<Obj, Key>; declare function hasOwnProp<Obj extends unknown[] | AnyFunction, Key extends PropertyKey>(obj: Obj, key: Key, options?: HasOwnPropOptions<boolean, boolean>): obj is SmartDetectArrayFuncHasOwnProp<Obj, Key>; declare function hasOwnProp<Obj extends unknown | AnyFunction, Key extends PropertyKey, DiscardUndefined extends boolean = true, DiscardNull extends boolean = false>(obj: Obj, key: Key | "length" | (IsPlainObjectResult<Obj> extends never ? never : keyof Obj), options?: HasOwnPropOptions<DiscardUndefined, DiscardNull> /** @ts-expect-error we force to override recursive type result */ ): obj is SmartDetectUnknownKeyHasOwnProp<Obj, Key, DiscardUndefined, DiscardNull>; /** ------------------- * * ***Type guard: `isArguments`.*** * ------------------- * **Checks if `value` is likely an `arguments` object.** * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is an ***[`IArguments`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments)*** object, else `false`. * @example * isArguments(function() { return arguments; }()); * // ➔ true * isArguments([1, 2, 3]); * // ➔ false */ declare const isArguments: (value: unknown) => value is IArguments; /** ---------------------------------------------------------- * * ***Type guard: `isArray`.*** * ---------------------------------------------------------- ***Checks if a value is an ***[`Array`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)***.** * - **Behavior:** * - Uses ***[`Array.isArray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray)*** for reliable and safe type checking. * - Supports TypeScript **type narrowing** using `value is T[]`. * - Handles edge cases like `null`, `undefined`, and non-array objects. * @template T - The expected type of array elements. * @param {*} value - The value to check. * @returns {boolean} Returns `true` if the value is an `array`, otherwise `false`. * @example * isArray([1, 2, 3]); * // ➔ true * isArray([]); * // ➔ true * isArray("hello"); * // ➔ false * isArray({ key: "value" }); * // ➔ false * isArray(null); * // ➔ false * isArray(undefined); * // ➔ false */ declare function isArray<T extends unknown[]>(value: T): value is Extract<T, unknown[]>; declare function isArray<T extends readonly unknown[]>(value: T): value is Extract<T, readonly unknown[]>; declare function isArray(value: unknown): value is unknown[]; /** ---------------------------------------------------- * * ***Type guard: `isArrayBuffer`.*** * ---------------------------------------------------- * **Checks if `value` is classified as an `ArrayBuffer` object.** * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is instance of ***[`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)***, else `false`. * @example * isArrayBuffer(new ArrayBuffer(2)); * // ➔ true * isArrayBuffer(new Array(2)); * // ➔ false */ declare function isArrayBuffer(value: unknown): value is ArrayBuffer; /** ---------------------------------------------------- * * ***Type guard: `isArrayLike`.*** * ---------------------------------------------------- * **Checks if `value` is array-like, a value is considered array-like if it's * not a function and has a `value.length` that's an integer greater than or * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.** * @template T - The type of the value being checked. * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is array-like, else `false`. * @example * isArrayLike([1, 2, 3]); * // ➔ true * isArrayLike(document.body.children); * // ➔ true * isArrayLike(noop); * // ➔ false * isArrayLike('abc'); * // ➔ false */ declare function isArrayLike<T extends { __anyHack: unknown; }>(value: T): boolean; declare function isArrayLike(value: AnyFunction | null | undefined): value is never; declare function isArrayLike(value: unknown): value is { length: number; }; /** ---------------------------------------------------- * * ***Type guard: `isArrayLikeObject`.*** * ---------------------------------------------------- * **This method is like ***`isArrayLike` utility function*** except that * it also checks if `value` is an object.** * @template T - The type of the value being checked. * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is `array-like object`, else `false`. * @example * isArrayLikeObject([1, 2, 3]); * // ➔ true * isArrayLikeObject(document.body.children); * // ➔ true * isArrayLikeObject('abc'); * // ➔ false * isArrayLikeObject(noop); * // ➔ false */ declare function isArrayLikeObject<T extends { __anyHack: unknown; }>(value: T): boolean; declare function isArrayLikeObject(value: AnyFunction | string | boolean | number | null | undefined): value is never; declare function isArrayLikeObject(value: unknown): value is object & { length: number; }; /** ---------------------------------------------------------- * * ***Type guard: `isBigInt`.*** * ---------------------------------------------------------- * **Checks if a value is of type **[`BigInt`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt)**.** * - **Behavior:** * - Uses `typeof value === "bigint"` for strict type checking. * - Supports TypeScript type narrowing with `value is bigint`. * - Returns `false` for `BigInt` object (object-wrapped), e.g: * - `Object(BigInt(123))`. * @param {*} value - The value to check. * @returns {boolean} Return `true` if value is a primitive bigint. * @example * isBigInt(123n); * // ➔ true * isBigInt(BigInt(123)); * // ➔ true * isBigInt("123"); * // ➔ false * isBigInt(Object(BigInt(1))); * // ➔ false */ declare const isBigInt: (value: unknown) => value is bigint; /** ---------------------------------------------------------- * * ***Type guard: `isBoolean`.*** * ---------------------------------------------------------- * **Checks if a value is of type **[`boolean`](https://developer.mozilla.org/en-US/docs/Glossary/Boolean/JavaScript)**.** * @param {*} value - The value to check. * @returns {boolean} Returns `true` if the value is a `boolean`, otherwise `false`. * @example * isBoolean(true); // ➔ true * isBoolean(false); // ➔ true * isBoolean("true"); // ➔ false */ declare const isBoolean: (value: unknown) => value is boolean; /** ---------------------------------------------------- * * ***Type guard: `isBooleanObject`.*** * ---------------------------------------------------- * **Checks if a value is a **`Boolean` object wrapper** * (`new Boolean(...)`), not a primitive boolean.** * @param {*} value The value to check. * @returns {value is Boolean} Returns `true` if `value` is a `Boolean` object. * @example * isBooleanObject(new Boolean(true)); * // ➔ true * isBooleanObject(true); * // ➔ false */ declare function isBooleanObject(value: unknown): value is Boolean; /** ---------------------------------------------------- * * ***Type guard: `isBuffer`.*** * ---------------------------------------------------------- * **Checks if a value is a *****{@link Buffer | `Node.js - Buffer`}***** instance.** * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is a `Buffer`, else `false`. * @example * isBuffer(new Buffer(2)); * // ➔ true * isBuffer(Buffer.alloc(10)); * // ➔ true * isBuffer(Buffer.from('foo')); * // ➔ true * isBuffer([]); * // ➔ false * isBuffer('a string'); * // ➔ false * isBuffer(new Uint8Array(1024)); * // ➔ false */ declare const isBuffer: (value: unknown) => value is Buffer; /** ----------------------------------------------------------- * * ***Predicate: `isCurrencyLike`.*** * ----------------------------------------------------------- * **Determines if the given `input` can be interpreted as a currency-like number, * using the same **multi-locale parsing logic** as ***`parseCurrencyString`***.** * - **Highlights:** * - *Supports strings or numbers like:* * - `"15.000,10"` ***(European)***. * - `"15,000.10"` ***(US)***. * - `"15'000.10"` ***(Swiss)***. * - `"15 000,10"` ***(French)***. * - `"Rp 15.000,10"` or `"$15,000.10"`. * - Also accepts simple numbers (`15300.95`). * - **ℹ️ Note:** * - Uses the same core logic as * ***`parseCurrencyString`*** but * just checks if a final parsed float is sensible. * @param {*} input - The input value to check. * @returns {boolean} Return `true` if it can be reasonably parsed into a currency-like number, `false` otherwise. * @example * isCurrencyLike(15300.95); * // ➔ true * isCurrencyLike("$15,000.10"); * // ➔ true * isCurrencyLike("(15'000.10)"); * // ➔ true * isCurrencyLike("Rp 15.000,10"); * // ➔ true * isCurrencyLike(""); * // ➔ false * isCurrencyLike("abc"); * // ➔ false */ declare const isCurrencyLike: (input: unknown) => boolean; type isDateOptions = { /** * ***Skip the validity check (`!isNaN(date.getTime())`).*** * * When `true`, the function only checks that the value is an * instance of `Date` without verifying that it represents a valid * date value, default: `false`. * * @default false */ skipInvalidDate?: boolean; }; /** ---------------------------------------------------------- * * ***Type guard: `isDate`.*** * ---------------------------------------------------------- * **Determines whether the given `value` is a real, valid JavaScript * **[`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date)** object.** * - **Behavior:** * - Returns **true** only if: * - `value` is an instance of `Date`. * - and, unless `options.skipInvalidDate` is `true`, * the underlying time value is valid (`!isNaN(value.getTime())`). * - Returns **false** for: * - non-Date values (strings, numbers, etc.). * - `Date` instances that represent an invalid time value * (e.g. `new Date("bad")`), unless skipping is enabled. * @param {*} value - The value to check. * @param {isDateOptions} [options] - Optional settings. * @returns {boolean} Return `true` if value is a valid Date object. * @example * isDate(new Date()); * // ➜ true * isDate(new Date("invalid")); * // ➜ false * isDate("2024-01-01"); * // ➜ false * * // Skipping validity check: * isDate(new Date("invalid"), { skipInvalidDate: true }); * // ➜ true */ declare const isDate: (value: unknown, options?: isDateOptions) => value is Date; /** ---------------------------------------------------------- * * ***Predicate: `isDeepEqual`.*** * ---------------------------------------------------------- * **Performs a deep equality check between two values.** * - **Behavior:** * - Compares nested `arrays`, `objects`, `Dates`, `RegExp`, `NaN`, `Symbols`, * `Set`, and `Map`. * - Handles special cases: * - `NaN` is considered equal to `NaN`. * - `Date` objects are equal if `.getTime()` is equal. * - `RegExp` objects are equal if `.toString()` is equal. * - `Symbol("x")` and `Symbol("x")` are treated equal if * `.toString()` matches. * - `Set` and `Map` are deeply compared by content (order-insensitive). * - **ℹ️ Note:** * - Does not support circular references. * @param {*} a - First value to compare. * @param {*} b - Second value to compare. * @returns {boolean} `true` if both values are deeply equal, otherwise `false`. * @example * // ✅ Primitives * isDeepEqual(1, 1); * // ➔ true * isDeepEqual(NaN, NaN); * // ➔ true * isDeepEqual("hello", "world"); * // ➔ false * * // ✅ Objects * isDeepEqual({ x: 1 }, { x: 1 }); * // ➔ true * isDeepEqual({ x: 1 }, { y: 1 }); * // ➔ false * * // ✅ Arrays * isDeepEqual([1, 2], [1, 2]); * // ➔ true * isDeepEqual([1, 2], [2, 1]); * // ➔ false * * // ✅ Dates * isDeepEqual(new Date(123), new Date(123)); * // ➔ true * * // ✅ Sets * isDeepEqual(new Set([1, 2]), new Set([2, 1])); * // ➔ true * * // ✅ Maps * isDeepEqual(new Map([["a", 1]]), new Map([["a", 1]])); * // ➔ true * * // ❌ Different types * isDeepEqual(1, "1"); * // ➔ false */ declare const isDeepEqual: (a: unknown, b: unknown) => boolean; /** ---------------------------------------------------- * * ***Type guard: `isElement`.*** * ---------------------------------------------------------- * **Checks if `value` is likely a * **[`DOM Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element)**.** * @template T - The type of the value being checked. * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is extends instance of **[`Element`](https://developer.mozilla.org/en-US/docs/Web/API/Element)**, else `false`. * @example * isElement(document.body); * // ➔ true * isElement(document.createElement("div")); * // ➔ true * isElement('<body>'); * // ➔ false * isElement(document); * // ➔ false * isElement({ tagName: "DIV" }); * // ➔ false */ declare function isElement(value: []): value is []; declare function isElement<T extends Element>(value: T): value is T; declare function isElement(value: unknown): value is Element; /** * ------------------------------------------------------------------- * * ***Array-like structure interface.*** * ------------------------------------------------------------------- * **Represents objects with indexed elements and a `length` property, * similar to arrays, but not necessarily full Array instances.** * @template T The type of elements stored in the array-like object. * @example * ```ts * function logArrayLike<T>(items: ArrayLike<T>) { * for (let i = 0; i < items.length; i++) { * console.log(items[i]); * } * } * * const myNodeList: ArrayLike<Element> = document.querySelectorAll("div"); * logArrayLike(myNodeList); * ``` */ interface ArrayLike<T> { /** * ***Number of elements in the array-like object.*** */ readonly length: number; /** * ***Indexed access to elements.*** */ readonly [n: number]: T; } /** ------------------------------------------------------------------- * * ***Represents an object with no allowed properties.*** * ------------------------------------------------------------------- * **Useful for cases where you want to ensure a type has **no keys**.** * @template T - The base type to convert into an empty object type. * @example * ```ts * type Test = EmptyObject<{ a: number }>; * // ➔ { a?: never } * ``` */ type EmptyObject<T> = { [K in keyof T]?: never; }; /** ------------------------------------------------------------------- * * ***Conditional empty object type.*** * ------------------------------------------------------------------- * **Produces `EmptyObject<T>` only if it is assignable to `T`.** * @template T - The base type to check. * @example * ```ts * type Test = EmptyObjectOf<{ a: number }>; * // ➔ { a?: never } | never depending on assignability * ``` */ type EmptyObjectOf<T> = EmptyObject<T> extends T ? EmptyObject<T> : never; /** ------------------------------------------------------------------- * * ***List type alias.*** * ------------------------------------------------------------------- * **Represents any array-like structure.** * @template T - The type of elements in the list. * @example * ```ts * const arr: List<number> = [1, 2, 3]; * const nodeList: List<Element> = document.querySelectorAll("div"); * ``` */ type List<T> = ArrayLike<T>; /** ---------------------------------------------------- * * ***Predicate: `isEmpty`.*** * ---------------------------------------------------------- * **Checks if `value` is an empty object, collection, map, or set.** * - **Behavior:** * - **Objects** are empty if they have no own enumerable string keyed properties. * - **Array-like values** (arrays, strings, `arguments`, typed arrays, buffers) * are empty if their `length` is `0`. * - **Maps** and **Sets** are empty if their `size` is `0`. * - **Booleans**, **numbers** (including `NaN`), **symbols**, and `null`/ * `undefined` are treated as empty. * - **Functions** are considered empty if they have no own enumerable keys. * - **ℹ️ Note:** * - For more `Strict`, you can use * ***`isEmptyValue` utility function*** instead. * @template T - The type of the value being checked. * @param {*} value The value to check. * @returns {boolean} Returns `true` if `value` is empty, else `false`. * @example * isEmpty(null); * // ➔ true * isEmpty(true); * // ➔ true * isEmpty(false); * // ➔ true * isEmpty(1); * // ➔ true * isEmpty(0); * // ➔ true * isEmpty(Symbol("x")); * // ➔ true * isEmpty(() => {}); * // ➔ true * isEmpty(""); * // ➔ true * isEmpty(" "); * // ➔ false * isEmpty([1, 2, 3]); * // ➔ false * isEmpty({ 'a': 1 }); * // ➔ false */ declare function isEmpty<T extends { __trapAny: any; }>(value?: T): boolean; declare function isEmpty(value: string): value is ""; declare function isEmpty(value: Map<any, any> | Set<any> | List<any> | null | undefined): boolean; declare function isEmpty(value: object): boolean; declare function isEmpty<T extends object>(value: T | null | undefined): value is EmptyObjectOf<T> | null | undefined; declare function isEmpty(value: any): boolean; /** ---------------------------------------------------------- * * ***Predicate: `isEmptyArray`.*** * ---------------------------------------------------------- * **Checks whether a given value is an empty array.** * - **Behavior:** * - Non-array inputs are considered ***`empty`*** ***(defensive strategy)***. * @param {*} [value] - The value to check. * @returns {boolean} Returns `true` if it's ***not an array*** or ***an empty-array***. * @example * isEmptyArray([]); // ➔ true * isEmptyArray([1, 2, 3]); // ➔ false * isEmptyArray("not an array"); // ➔ true * isEmptyArray(null); // ➔ true * isEm