@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.
997 lines (977 loc) • 98.5 kB
TypeScript
/*!
* ====================================================
* Rzl Utils-JS.
* ----------------------------------------------------
* Version: 3.11.0.
* Author: Rizalvin Dwiky.
* Repository: https://github.com/rzl-zone/utils-js.
* ====================================================
*/
import { Nullish, KeepNull, KeepUndef, AnyFunction, NormalizeEmptyArraysRecursive, RemoveEmptyArrayElements, FixNeverArrayRecursive, IfNotExtends, IfExtends, IsAny, NumberRangeUnion, Prettify, OrArr, Extends, AndArr, TypedArray, WebApiObjects, IntlObjects } from '@rzl-zone/ts-types-plus';
type NormalizeInputToNumberArrayUnRecursive<T> = T extends string | bigint | boolean | number | Nullish ? T : HasNonNumberLikeNonNullish<T>;
/** Detects whether `T` contains any number-like type (`string | number | bigint`).
*
* @template T Input type.
* @returns `true` if `T` contains number-like, otherwise `false`.
*/
type HasNumberLike<T> = [Extract<T, string | bigint | number>] extends [never] ? false : true;
/** Detects whether `T` contains a string type.
*
* - Useful for identifying values that may fail parsing to number (`undefined` when `R=false`).
*
* @template T Input type.
* @returns `true` if `T` contains `string`, otherwise `false`.
*/
type HasString<T> = [Extract<T, string>] extends [never] ? false : true;
/** Detects whether `T` contains non-number-like, non-nullish values (`objects`, `arrays`, `symbols`, `functions`, `etc`.).
*
* @template T Input type.
* @returns `true` if such types exist, otherwise `false`.
*/
type HasNonNumberLikeNonNullish<T> = [
Exclude<T, string | bigint | number | Nullish>
] extends [never] ? false : true;
/** -------------------------------------------------------
* * ***Computes the return type of {@link toNumberArrayUnRecursive|`toNumberArrayUnRecursive`}
* based on input type `T` and option `R`.***
* -------------------------------------------------------
*
* **Behavior:**
* - If `R = true` (`removeInvalidValueNumber: true`):
* - If `T` is only `null | undefined` ➔ returns `[]`.
* - If `T` contains number-like (`string | number | bigint`) ➔ returns `number[]`.
* - Otherwise ➔ returns `[]`.
* - If `R = false` (`removeInvalidValueNumber: false`):
* - Preserves `null[]` or `undefined[]` if input is purely nullish.
* - Otherwise returns an array of:
* - `number` if `T` includes number-like.
* - `undefined` if parsing fails (string or invalid non-number-like).
* - Original `null` / `undefined` from input.
* @template T Input element type.
* @template R Flag indicating whether invalid values should be removed (`true`) or kept (`false`).
*
*/
type ToNumberArrayUnRecursiveReturn<T, R extends boolean> = R extends true ? [
Exclude<T, null | undefined>
] extends [never] ? [] : HasNumberLike<T> extends true ? number[] : [] : [
Exclude<T, null>
] extends [never] ? null[] : [Exclude<T, undefined>] extends [never] ? undefined[] : Array<(HasNumberLike<T> extends true ? number : never) | (HasString<T> extends true ? undefined : never) | (HasNonNumberLikeNonNullish<T> extends true ? undefined : never) | KeepNull<T> | KeepUndef<T>>;
/** -------------------------------------------------------
* * ***Options for {@link toNumberArrayUnRecursive|`toNumberArrayUnRecursive`}.***
* -------------------------------------------------------
*
* @template T Flag indicating whether invalid values should be removed.
*/
type ToNumberArrayUnRecursiveOptions<T extends boolean> = {
/** If true, removes invalid number values (`NaN`, non-numeric strings, `null`, `undefined`) from the result, defaultValue: `true`.
*
* @default true
*/
removeInvalidValueNumber?: T;
};
/** -------------------------------------------------------
* * ***Utility: `toNumberArrayUnRecursive`.***
* -------------------------------------------------------
* **Converts a flat array of strings, numbers, nulls, or undefineds into numbers.**
* - **Behavior:**
* - Only supports **flat arrays** (non-recursive).
* - Valid inputs: `string`, `number`, `null`, `undefined`.
* - `BigInt` will be converted to `number`.
* - Other values ➔ coerced into `undefined`.
* - Invalid values can be **removed** (`removeInvalidValueNumber: true`) or **kept** (`false`).
* - **ℹ️ Note:**
* - _For recursive / nested arrays, use ***`toNumberDeep` utility function*** instead._
* @template T - Element type of the input array.
* @template R - Whether invalid values should be removed (`true`) or kept (`false`).
* @param {Array<T> | readonly T[] | null | undefined} [array] - The array to convert, returns `undefined` if not an array.
* @param {ToNumberArrayUnRecursiveOptions<RemoveInvalidValue>} [options] - Options to control transformation behavior, defaults to `{ removeInvalidValueNumber: true }`.
* @returns {ToNumberArrayUnRecursiveReturn<NormalizeInput<T>, RemoveInvalidValue>} A new array of string representations, with invalid values optionally removed.
* @example
* ```ts
* toNumberArrayUnRecursive(['1', 2, '3']);
* // ➔ [1, 2, 3]
* toNumberArrayUnRecursive([1, null, undefined, 'abc']);
* // ➔ [1]
* toNumberArrayUnRecursive(['1', null, undefined, 'abc'], {
* removeInvalidValueNumber: false
* });
* // ➔ [1, null, undefined, undefined]
* toNumberArrayUnRecursive(null); // ➔ undefined
* toNumberArrayUnRecursive(undefined); // ➔ undefined
* toNumberArrayUnRecursive(1); // ➔ undefined
* ```
*/
declare function toNumberArrayUnRecursive(array?: null | undefined, options?: ToNumberArrayUnRecursiveOptions<boolean>): undefined;
declare function toNumberArrayUnRecursive(array?: Array<never>, options?: ToNumberArrayUnRecursiveOptions<boolean>): [];
declare function toNumberArrayUnRecursive<T, R extends boolean = true>(array?: Array<T> | readonly T[] | null, options?: ToNumberArrayUnRecursiveOptions<R>): ToNumberArrayUnRecursiveReturn<NormalizeInputToNumberArrayUnRecursive<T>, R>;
declare function toNumberArrayUnRecursive<T = unknown>(array?: T, options?: ToNumberArrayUnRecursiveOptions<boolean>): undefined;
/** Union of primitive types that can be safely converted to string. */
type AllowedToString = string | number | boolean | bigint;
/** Checks whether `T` contains any type that is allowed for conversion to string.
*
* - Returns `true` if `T` contains `string`, `number`, `boolean`, or `bigint`.
* - Otherwise returns `false`.
*
* @template T Input type to check.
*/
type HasAllowed<T> = [Extract<T, AllowedToString>] extends [never] ? false : true;
/** Checks whether `T` contains any non-nullish value that is disallowed for conversion.
*
* - Disallowed non-nullish types include `objects`, `arrays`, `symbols`, `functions`, `etc`.
* - Returns `true` if such types exist, otherwise `false`.
* @template T Input type to check.
*/
type HasDisallowedNonNullish<T> = [Exclude<T, AllowedToString | Nullish>] extends [never] ? false : true;
/** -------------------------------------------------------
* * ***Computes the return type of {@link toStringArrayUnRecursive|`toStringArrayUnRecursive`}
* based on input type `T` and option `R`.***
* -------------------------------------------------------
*
* **Behavior:**
* - If `R = true` (`removeInvalidValue = true`):
* - If `T` contains any allowed values ➔ `string[]`.
* - If `T` contains only nullish or disallowed types ➔ `[]`.
*
* - If `R = false` (`removeInvalidValue = false`):
* - Include `string` if `T` has allowed values.
* - Include `undefined` if `T` has disallowed non-nullish values.
* - Preserve `null` and `undefined` from original `T`.
* @template T Input element type.
* @template R Flag indicating whether invalid values should be removed (`true`) or kept (`false`).
*/
type ToStringArrayUnRecursiveReturn<T, R extends boolean> = R extends true ? HasAllowed<T> extends true ? string[] : [] : Array<(HasAllowed<T> extends true ? string : never) | (HasDisallowedNonNullish<T> extends true ? undefined : never) | KeepNull<T> | KeepUndef<T>>;
/** -------------------------------------------------------
* * ***Options for {@link toStringArrayUnRecursive|`toStringArrayUnRecursive`}.***
* -------------------------------------------------------
*
* @template T Flag indicating whether invalid values should be removed.
*/
type ToStringArrayUnRecursiveOptions<T extends boolean> = {
/** If true, removes invalid values (`null` and `undefined`) from the output, defaultValue: `true`.
*
* @default true
*/
removeInvalidValue?: T;
};
/** ---------------------------------------------
* * ***Utility: `toStringArrayUnRecursive`.***
* ---------------------------------------------
* **Converts all values in a flat array into string representations.**
* - **Behavior:**
* - Only processes **flat arrays** (non-recursive).
* - Supports input values: `string`, `number`, `bigint`, `boolean`,
* `null`, `undefined`.
* - Invalid values (`null` and `undefined`) can be **removed** or **kept**
* depending on the option.
* - Other unsupported types will be converted to `undefined` (and removed
* if `removeInvalidValue=true`).
* - **ℹ️ Note:**
* - _For recursive / nested arrays, use ***`toStringDeep` utility function*** instead._
* @template T - Element type of the input array.
* @template R - Whether invalid values should be removed (`true`) or kept (`false`).
* @param {Array<string | number | bigint | boolean | null | undefined> | null | undefined} [array] - The array to convert, returns `undefined` if not an array.
* @param {ToStringArrayUnRecursiveOptions<RemoveInvalidValue>} [options] - Options to control transformation behavior, defaults to `{ removeInvalidValue: true }`.
* @param {RemoveInvalidValue extends true ? boolean : boolean} [options.removeInvalidValue=true] Whether to remove invalid values (`null`, `undefined`, or unsupported types), default: `true`.
* @returns {RemoveInvalidValue extends true ? string[] : (string | null | undefined)[]} A new array of string representations, with invalid values optionally removed.
* @example
* ```ts
* // Convert numbers and strings
* toStringArrayUnRecursive([1, 2, '3']);
* // ➔ ['1', '2', '3']
* // Remove null and undefined
* toStringArrayUnRecursive([1, null, undefined, 'abc'], {
* removeInvalidValue: true
* });
* // ➔ ['1', 'abc']
* // Keep null and undefined
* toStringArrayUnRecursive([1, null, undefined, 'abc'], {
* removeInvalidValue: false
* });
* // ➔ ['1', null, undefined, 'abc']
* // Convert boolean and bigint
* toStringArrayUnRecursive([true, false, 10n]);
* // ➔ ['true', 'false', '10']
* // Not an array ➔ returns undefined
* toStringArrayUnRecursive(null);
* // ➔ undefined
* toStringArrayUnRecursive(undefined);
* // ➔ undefined
* toStringArrayUnRecursive(1);
* // ➔ undefined
* toStringArrayUnRecursive("string");
* // ➔ undefined
* ```
*/
declare function toStringArrayUnRecursive(array?: undefined | null, options?: ToStringArrayUnRecursiveOptions<boolean>): undefined;
declare function toStringArrayUnRecursive(array?: Array<never>, options?: ToStringArrayUnRecursiveOptions<boolean>): [];
declare function toStringArrayUnRecursive<T, R extends boolean = true>(array?: Array<T> | readonly T[] | null, options?: ToStringArrayUnRecursiveOptions<R>): ToStringArrayUnRecursiveReturn<T, R>;
declare function toStringArrayUnRecursive<T = unknown>(array?: T, options?: ToStringArrayUnRecursiveOptions<boolean>): undefined;
type ResUnFTN<Force extends false | "stringOrNumber" | "primitives" | "all" = false> = Force extends "all" ? Array<unknown[] | Record<string, unknown> | string> : Force extends "stringOrNumber" ? Array<string | boolean | bigint | symbol | null | undefined | Record<string, unknown> | AnyFunction | unknown[] | Date | RegExp | Map<unknown, unknown> | Set<unknown> | Promise<unknown>> : Force extends "primitives" ? Array<string | symbol | Record<string, unknown> | AnyFunction | unknown[] | Date | RegExp | Map<unknown, unknown> | Set<unknown> | Promise<unknown>> : Force extends false ? Array<string | number | bigint | boolean | symbol | RegExp | Record<string, unknown> | AnyFunction | Date | Map<unknown, unknown> | Set<unknown> | Promise<unknown> | unknown[] | null | undefined> : unknown[];
type ResFTN<Force extends false | "stringOrNumber" | "primitives" | "all" = false> = Force extends "all" ? Array<string | Record<string, unknown>> : Force extends "stringOrNumber" ? Array<string | boolean | bigint | symbol | null | undefined | Record<string, unknown> | AnyFunction | Date | RegExp | Promise<unknown>> : Force extends "primitives" ? Array<string | symbol | RegExp | Record<string, unknown> | AnyFunction | Date | Promise<unknown>> : Force extends false ? Array<string | number | bigint | boolean | symbol | RegExp | Record<string, unknown> | AnyFunction | Date | Promise<unknown> | null | undefined> : unknown[];
type DedupeResult<Force extends ForceToStringOptions = false, FTN extends boolean = false> = FTN extends false ? ResUnFTN<Force> : ResFTN<Force>;
type ForceToStringOptions = false | "stringOrNumber" | "primitives" | "all";
type DedupeArrayOptions<F extends ForceToStringOptions, Fl extends boolean> = {
/** Enables string conversion for comparison, default is `false`.
*
* @default false
* @type {ForceToStringOptions}
*/
forceToString?: F;
/** If true, deeply flattens `Arrays`, `Maps`, and `Sets` before deduplication, default is `false`.
*
* @default false
*/
flatten?: Fl;
};
/** ----------------------------------------------------------
* * ***Utility: `dedupeArray`.***
* ---------------------------------------------
* **Deduplicates values in an array (with optional flattening and deep stringification).**
* - Supports various modes for converting values to strings before deduplication:
* - `"stringOrNumber"`: Converts strings and numbers to strings.
* - `"primitives"`: Converts all primitives (string, number, boolean, bigint, null, undefined, NaN) to strings.
* - `"all"`: Converts all values (primitives, objects, Maps, Sets, Symbols, RegExp, Dates, Errors, Promises, functions)
* to strings, including nested object properties.
* - `false` (default): No conversion applied.
* - Options:
* - `forceToString`: Enables string conversion for comparison, default is `false`.
* - `flatten`: If true, deeply flattens arrays, Maps, and Sets before deduplication, default is `false`.
* @template ForceToString - `forceToString` mode.
* @template Flattening - `flatten` mode.
* @param {unknown[]} inputArray - The array to deduplicate, can be deeply nested and contain any mix of types.
* @param {DedupeArrayOptions<ForceToString, Flattening>|undefined} [options] - Options to control string conversion.
* @returns {DedupeResult<ForceToString, Flattening>} Deduplicated array with optional transformations.
* @throws **{@link TypeError | `TypeError`}** if the input is not an array, or options is not an object, or if `forceToString` is invalid.
* @example
* ```ts
* dedupeArray(["apple", "banana", "apple"]);
* // ➔ ["apple", "banana"]
* dedupeArray([[1, 2], [1, 2]], { flatten: true });
* // ➔ [1, 2]
* dedupeArray([new Set([1, 2]), new Set([2, 3])], { flatten: true });
* // ➔ [1, 2, 3]
* dedupeArray([1, "1", 2, "2"], {
* forceToString: "stringOrNumber"
* }); // ➔ ["1", "2"]
* dedupeArray([true, "true", false, undefined], {
* forceToString: "primitives"
* }); // ➔ ["true", "false", "undefined"]
* dedupeArray([1, "1", { a: 1 }], {
* forceToString: "all"
* }); // ➔ ["1", { a: "1" }]
* dedupeArray([1, 1, [2, 2, [3, 3]]]);
* // ➔ [1, [2, [3]]]
* dedupeArray([null, undefined, null]);
* // ➔ [null, undefined]
* dedupeArray([[], [[]], [[[]]], [[]], [[[]]]]);
* // ➔ [[], [[]], [[[]]]]
* const fn = () => 1;
* dedupeArray([fn, fn, () => 1]);
* // ➔ [fn, () => 1] cause: ref () => 1 and fn is different but ref const `fn` and `fn` is same ref.
* dedupeArray([Symbol("x"), Symbol("x")]);
* // ➔ [Symbol("x")] (symbols are same by identity, so dedupe
* dedupeArray([NaN, NaN, 1, "1"]);
* // ➔ [NaN, 1, "1"]
* dedupeArray([NaN, NaN, 1, "1"], {
* forceToString: "primitives"
* }); // ➔ ["NaN", "1"]
* dedupeArray([new Date("2025-01-01"), new Date("2025-01-01")]);
* // ➔ [Date("2025-01-01")] (same time, deduped)
* dedupeArray([new Date("2025-01-01"), new Date("2025-01-01")], {
* forceToString: "all"
* }); // ➔ ["2025-01-01T00:00:00.000Z"]
* dedupeArray([/abc/, /abc/], {
* forceToString: "all"
* }); // ➔ ["/abc/"]
* dedupeArray([new Map(), new Set(), new Error("err")], {
* forceToString: "all"
* }); // ➔ ["[object Map]", "[object Set]", "Error: err"]
* dedupeArray([Promise.resolve(1), Promise.resolve(1)], {
* forceToString: "all"
* }); // ➔ ["[object Promise]"]
* dedupeArray([{ a: 1 }, { a: 1 }, { a: 2 }], {
* forceToString: "primitives"
* }); // ➔ [{ a: "1" }, { a: "2" }]
* dedupeArray([{ a: { b: 1 } }, { a: { b: 1 } }], {
* forceToString: "all"
* }); // ➔ [{ a: { b: "1" } }]
* dedupeArray("not an array");
* // ➔ Throws TypeError
* dedupeArray([1, 2, 3], {
* forceToString: "invalid"
* }); // ➔ Throws TypeError
* ```
*/
declare const dedupeArray: <ForceToString extends ForceToStringOptions = false, Flattening extends boolean = false>(inputArray: unknown[], options?: DedupeArrayOptions<ForceToString, Flattening>) => DedupeResult<ForceToString, Flattening>;
type ExcludeNil<T> = Exclude<T, null | undefined>;
/** ----------------------------------------------------------
* * ***Element extractor***
* ----------------------------------------------------------
*/
type ElementOf<A extends readonly unknown[]> = A extends readonly (infer U)[] ? U : never;
/** ----------------------------------------------------------
* * ***Compute `FilterNilArray`***
* ----------------------------------------------------------
*
* for a tuple/array A by using the element type (without null|undefined).
*
*/
type FilterNilArrayFromTuple<A extends readonly unknown[]> = FilterNilArray<ExcludeNil<ElementOf<A>>>;
/** ----------------------------------------------------------
* ***Preserve `mutability`: if A is mutable (extends unknown[]), keep B; otherwise make B readonly***. */
type PreserveMutability<A extends readonly unknown[], B> = A extends unknown[] ? B : Readonly<B>;
type IsDeepEmptyArray<T> = T extends readonly [] ? true : T extends readonly (infer U)[] ? IsDeepEmptyArray<U> : false;
type FilterNilRecursive<T> = T extends readonly (infer U)[] ? T extends (infer U)[] ? FilterNilRecursive<ExcludeEmptyArray<U>>[] : readonly FilterNilRecursive<ExcludeEmptyArray<U>>[] : Exclude<T, null | undefined>;
type ExcludeEmptyArray<T> = T extends [] ? never : T;
type NormalizerArrays<T> = NormalizeEmptyArraysRecursive<RemoveEmptyArrayElements<FilterNilRecursive<T[]>>>;
type FilterNilArray<T> = IsDeepEmptyArray<NormalizerArrays<T>> extends true ? [] : FixNeverArrayRecursive<NormalizerArrays<T>>;
/** ----------------------------------------------------------
* * ***Utility: `filterNilArray`.***
* ---------------------------------------------
* **Removes `null` and `undefined` values from an array, including nested arrays.**
* - **Behavior:**
* - Returns `undefined` if the input is explicitly `undefined` or `null`.
* - Returns `[]` if input is empty or all elements are removed after filtering.
* - Recursively filters nested arrays while preserving structure.
* - Ensures proper type inference for safer downstream operations.
* @template A - The type of elements in the array.
* @param {T[]|null|undefined} input - The array to be filtered.
* @returns {T[] | undefined} A new array with `null` and `undefined` values removed,
* or `undefined` if the input is explicitly `undefined` or `null`.
* @example
* ```ts
* filterNilArray([1, null, 2, undefined, 3]);
* // ➔ [1, 2, 3]
* filterNilArray([null, undefined]);
* // ➔ []
* filterNilArray(undefined);
* // ➔ undefined
* filterNilArray(null);
* // ➔ undefined
* filterNilArray([]); // or
* filterNilArray([[[]]]); // or
* filterNilArray([[[],undefined,null]]);
* // ➔ []
* filterNilArray([1, [null, 2, [undefined, 3]]]);
* // ➔ [1, [2, [3]]]
* ```
*/
declare function filterNilArray(input: null | undefined): undefined;
declare function filterNilArray<A extends readonly unknown[]>(input: A): PreserveMutability<A, FilterNilArrayFromTuple<A>>;
declare function filterNilArray<A extends readonly unknown[]>(input: A | null | undefined): PreserveMutability<A, FilterNilArrayFromTuple<A>> | undefined;
declare function filterNilArray<A>(input: (A | null | undefined)[] | null | undefined): FilterNilArray<A> | undefined;
declare function filterNilArray(input: readonly unknown[] | null | undefined): unknown[] | undefined;
declare function filterNilArray(input: unknown[]): unknown[];
/** ---------------------------------
* * ***Utility: `toBooleanContent`.***
* ---------------------------------------------
* **Converts a given value into a boolean (***strict***).**
* - **This is stricter than normal JS coercion:**
* - `null` and `undefined` return `false`.
* - Empty strings return `false`, non-empty strings return `true`.
* - Numbers: `0` is `false`, others `true`.
* - Booleans returned as-is.
* - Arrays: `[]` is `false`, non-empty is `true`.
* - Objects: `{}` is `false`, object with keys is `true`.
* @param {*} value - The value to be converted.
* @returns {boolean} Return `true` if the value is considered non-empty, otherwise `false`.
* @example
* toBooleanContent(null); // ➔ false
* toBooleanContent(undefined); // ➔ false
* toBooleanContent(""); // ➔ false
* toBooleanContent(" "); // ➔ false
* toBooleanContent("abc"); // ➔ true
* toBooleanContent(" asd "); // ➔ true
* toBooleanContent(0); // ➔ false
* toBooleanContent(42); // ➔ true
* toBooleanContent(NaN); // ➔ true
* toBooleanContent([]); // ➔ false
* toBooleanContent([1]); // ➔ true
* toBooleanContent({}); // ➔ false
* toBooleanContent({ a: 1 }); // ➔ true
* toBooleanContent({[Symbol("key")]: 123}); // ➔ false
*/
declare const toBooleanContent: (value: unknown) => boolean;
/** -------------------------------------------------
* * ***Utility: `toBooleanContentDeep`.***
* ---------------------------------------------
* **This function does a deep inspection to determine if the input
* contains any meaningful / non-empty value.**
* @description
* It is stricter than JavaScript's normal truthy checks because it looks *inside*
* nested arrays & objects (recursively checks).
* - **Rules:**
* - `null` and `undefined` return `false`
* - Empty strings `""` return `false`
* - `0` returns `false`
* - Empty arrays `[]` or empty objects `{}` return `false`
* - Checks deeply nested arrays/objects — if any value inside is "non-empty", returns `true`
* @param {*} value - The value to check.
* @returns {boolean} Return `true` if the value or anything nested inside is non-empty, otherwise `false`.
* @example
* toBooleanContentDeep(null); // ➔ false
* toBooleanContentDeep(""); // ➔ false
* toBooleanContentDeep(0); // ➔ false
* toBooleanContentDeep([]); // ➔ false
* toBooleanContentDeep({}); // ➔ false
* toBooleanContentDeep([[], {}]); // ➔ false
* toBooleanContentDeep("abc"); // ➔ true
* toBooleanContentDeep(42); // ➔ true
* toBooleanContentDeep(NaN); // ➔ true
* toBooleanContentDeep([0, "", 5]); // ➔ true
* toBooleanContentDeep([NaN, "", 0]); // ➔ true
* toBooleanContentDeep([0, "", null]); // ➔ false
* toBooleanContentDeep({ a: 0 }); // ➔ false
* toBooleanContentDeep({ a: 1 }); // ➔ true
* toBooleanContentDeep({ a: { b: [] }}); // ➔ false
* toBooleanContentDeep({ a: { b: "x" }}); // ➔ true
* toBooleanContentDeep({[Symbol("key")]: 123}); // ➔false
*/
declare const toBooleanContentDeep: (value: unknown) => boolean;
type ToBooleanExplicitOptions = {
/** Whether string comparison ignores case, _defaultValue: `false`_.
*
* @default false
*/
caseInsensitive?: boolean;
/** Whether to trim whitespace before comparison, _defaultValue: `true`_.
*
* @default true
*/
trimString?: boolean;
/** Whether to consider the string `"indeterminate"` as `true`, _defaultValue: `false`_.
*
* @default false
*/
includeIndeterminate?: boolean;
};
/** ---------------------------------
* * ***Utility: `toBooleanExplicit`.***
* ---------------------------------------------
* **Converts a value into a strict boolean.**
* - **Behavior:**
* - It supports various common string representations of truthy values,
* including `"true"`, `"on"`, `"yes"`, `"1"`, the number `1`, the boolean `true`,
* and optionally the string `"indeterminate"` if enabled.
* - **ℹ️ Note:**
* - Any other value, including `undefined`, `null`, `false`, `0`, and
* unrecognized strings will return `false`.
* - Supports optional `caseInsensitive` and `trimString` to customize
* string normalization.
* @param {*} value - The value to convert.
* @param {ToBooleanExplicitOptions} [options] - Options for conversion behavior.
* @param {ToBooleanExplicitOptions["caseInsensitive"]} [options.caseInsensitive=false] - Whether string comparison ignores case, default: `false`.
* @param {ToBooleanExplicitOptions["trimString"]} [options.trimString=true] - Whether to trim whitespace before comparison, default: `true`.
* @param {ToBooleanExplicitOptions["includeIndeterminate"]} [options.includeIndeterminate=false] - If `true`, the string `"indeterminate"` is considered a truthy value, defaults to `false`.
* @returns {boolean} Return `true` if the value matches a truthy representation, otherwise `false`.
* @throws **{@link TypeError | `TypeError`}** if any option provided is not a boolean.
* @example
* toBooleanExplicit(1);
* // ➔ true
* toBooleanExplicit(true);
* // ➔ true
* toBooleanExplicit("on");
* // ➔ true
* toBooleanExplicit("1");
* // ➔ true
* toBooleanExplicit(0);
* // ➔ false
* toBooleanExplicit("off");
* // ➔ false
* toBooleanExplicit("Yes");
* // ➔ false (caseInsensitive is false by default)
* toBooleanExplicit(" yes ");
* // ➔ true (whitespace trimmed by default)
* toBooleanExplicit("YES", { caseInsensitive: true });
* // ➔ true
* toBooleanExplicit("YES", { caseInsensitive: false });
* // ➔ false
* toBooleanExplicit(" YES ", { trimString: false });
* // ➔ false (whitespace not trimmed)
* toBooleanExplicit(" YES ", { trimString: true, caseInsensitive: true });
* // ➔ true
* toBooleanExplicit(" YES ", { trimString: true, caseInsensitive: false });
* // ➔ false
* toBooleanExplicit("indeterminate");
* // ➔ false (default)
* toBooleanExplicit("indeterminate", { includeIndeterminate: true });
* // ➔ true
*/
declare const toBooleanExplicit: (value: unknown, options?: ToBooleanExplicitOptions) => boolean;
/** ---------------------------------
* * ***Utility: `toBooleanLoose`.***
* ---------------------------------------------
* **Converts a given value into a boolean (loose).**
* - **This follows JavaScript's typical truthy/falsy rules with some tweaks:**
* - `null` and `undefined` return `false`.
* - Empty strings return `false`, non-empty strings return `true`.
* - Numbers: `0` is `false`, others `true`.
* - Booleans returned as-is.
* - Arrays: `[]` is `false`, non-empty is `true`.
* - Other objects: uses `Boolean(value)`, so `{}` is `true`.
* @param {*} value - The value to be converted.
* @returns {boolean} Return `true` if the value is truthy, otherwise `false`.
* @example
* toBooleanLoose(null); // ➔ false
* toBooleanLoose(""); // ➔ false
* toBooleanLoose("abc"); // ➔ true
* toBooleanLoose(0); // ➔ false
* toBooleanLoose(42); // ➔ true
* toBooleanLoose([]); // ➔ false
* toBooleanLoose([1]); // ➔ true
* toBooleanLoose({}); // ➔ true
* toBooleanLoose({ a: 1 }); // ➔ true
*/
declare const toBooleanLoose: (value: unknown) => boolean;
/** -------------------------------------------------------------
* * ***Utility: `parseCurrencyString`.***
* ---------------------------------------------
* **Parses a human-friendly currency string into a JavaScript number.**
* - **Supports multi-locale formats:**
* - ***US:*** `"15,000.10"` ➔ `15300.10`.
* - ***Swiss:*** `"15'000.10"` ➔ `15300.10`.
* - ***French:*** `"15 000,10"` ➔ `15300.10`.
* - ***Indian:*** `"1,23,456.78"` ➔ `123456.78`.
* - ***European:*** `"151.000,10"` ➔ `151300.10`.
* - ***Compact:*** `"15300000,10"` ➔ `15300000.10`.
* - **Features:**
* - Strips symbols automatically: `"Rp"`, `"$"`, `"EUR"`, `etc`.
* - Handles bracket negatives: `"(15.000,10)"` ➔ `-15300.10`.
* - Normalizes decimal separator (last dot or comma).
* - Detects non-breaking spaces (`\u00A0`, `\u202F`) often in European data.
* - Fallback to `0` for empty, invalid, or non-numeric strings.
* - **How it parses internally:**
* 1. Removes all characters except digits, `.`, `,`, `'`, `space`,
* `\u00A0`, `\u202F`.
* 2. Detects bracket (...) as negative.
* 3. If Indian style (`1,23,456`) detected by multiple ,`\d{2}`, removes all commas.
* 4. Otherwise:
* - If multiple dots & no commas ➔ thousands: removes all `.`.
* - If multiple commas & no dots ➔ thousands: removes all `,`.
* - If mixed, treats last `,` or `.` as decimal.
* 5. Converts final decimal to `.` for JS float.
* - **Gotchas:**
* - If both `.` and `,` are present, last occurrence is used as decimal.
* - For strings like `"1.121.234,56"` ➔ decimal is `,`.
* - For `"1,121,234.56"` ➔ decimal is `.`.
* - For `"15300000,2121"` ➔ decimal becomes `.` internally.
* - **ℹ️ Note:**
* - You can use this function as a first step to **sanitize currency inputs**
* before storing into database or doing math.
* - Always pair this with your formatter for consistent output display.
* @param {string|null|undefined} input
* ***Any messy currency string, may contain:***
* * Currency symbols (`Rp`,`$`, `CHF`, `EUR`).
* * Thousands separators (`.`, `,`, `'`, `space`, `\u00A0`, `\u202F`).
* * Various decimal formats (`,` or `.`).
* * Bracket negative: `"(15.000,10)"`.
* @returns {number} JavaScript float representation, will return `0` for invalid, empty, or non-string input.
* @example
* ```ts
* parseCurrencyString("Rp 15.300.000,21");
* // ➔ 15300000.21
* parseCurrencyString("15 300 000,21");
* // ➔ 15300000.21
* parseCurrencyString("CHF 15'300'000.21");
* // ➔ 15300000.21
* parseCurrencyString("$15,300,000.21");
* // ➔ 15300000.21
* parseCurrencyString("(15.000,10)");
* // ➔ -15000.10
* parseCurrencyString("1,23,456.78");
* // ➔ 123456.78
* parseCurrencyString("15300000,2121");
* // ➔ 15300000.2121
* parseCurrencyString("USD 15 300 000.21");
* // ➔ 15300000.21
* parseCurrencyString("");
* // ➔ 0
* parseCurrencyString("abc");
* // ➔ 0
* ```
*/
declare const parseCurrencyString: (input: string | null | undefined) => number;
/** ----------------------------------------------------------
* * ***Utility: `convertType`.***
* ---------------------------------------------
* **Converts a value from a string to its corresponding JavaScript primitive type.**
* - **Supported conversions for string inputs (case-insensitive, trimmed):**
* - `"true"` ➔ `true` (***boolean***).
* - `"false"` ➔ `false` (***boolean***).
* - `"null"` ➔ `null` (***null***).
* - `"yes"` ➔ `true` (***boolean***).
* - `"no"` ➔ `false` (***boolean***).
* - `"nan"` or `"NaN"` ➔ `NaN` (***number***).
* - `"undefined"` ➔ `undefined` (***undefined***).
* - Numeric strings with optional thousands separators (e.g. `"3,567,890.14"`) ➔ `3567890.14` ***as a `number` type***.
* - Strings containing only whitespace are converted to empty string `""`.
* - Non-string inputs are returned unchanged.
* - Strings not matching any special case are trimmed and returned as-is.
* @param {*} value - The value to convert, usually a string or other type.
* @returns {*} The converted JavaScript primitive (***`boolean`***, ***`number`***, ***`null`***, ***`undefined`***, ***`NaN`***) or the original value if no conversion applies.
* @example
* convertType("true"); // ➔ true
* convertType(" 42 "); // ➔ 42
* convertType("FALSE"); // ➔ false
* convertType(" null "); // ➔ null
* convertType(" "); // ➔ ""
* convertType(" Hello World "); // ➔ "Hello World"
* convertType("NaN"); // ➔ NaN
* convertType(100); // ➔ 100
* convertType(NaN); // ➔ NaN
* convertType({}); // ➔ {}
*/
declare const convertType: (value: unknown) => unknown;
/** --------------------------------------------------
* * ***Options for cleaning and transforming parsed JSON data.***
* --------------------------------------------------
*
* @private Type Options Validation for Function: {@link cleanParsedData | `cleanParsedData`}, {@link parseCustomDate | `parseCustomDate`} and {@link safeJsonParse | `safeJsonParse`}.
*/
type ParseParsedDataOptions = {
/** --------------------------------------------------
* * ***Convert numeric strings to numbers (e.g., `"42"` ➔ `42`), defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
convertNumbers?: boolean;
/** --------------------------------------------------
* * ***Convert numeric strings `"NaN"` to `NaN` (e.g., `"NaN"` ➔ `NaN`), defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
convertNaN?: boolean;
/** --------------------------------------------------
* * ***Convert `"true"` / `"false"` strings to boolean values, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
convertBooleans?: boolean;
/** --------------------------------------------------
* * ***Convert valid date strings into `Date` objects, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
convertDates?: boolean;
/** --------------------------------------------------
* * ***Custom date formats to be parsed (e.g., `["DD/MM/YYYY", "MM/DD/YYYY"]`), defaultValue: `[]`.***
* --------------------------------------------------
*
* @default []
*/
customDateFormats?: string[];
/** --------------------------------------------------
* * ***Remove `null` values from objects and arrays, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
removeNulls?: boolean;
/** --------------------------------------------------
* * ***Remove `undefined` values from objects and arrays, defaultValue: `false`.***
* --------------------------------------------------
*
* - ***Behavior:***
* - `false` (**default**): replaces `undefined` with `null`.
* - `true`: removes keys with `undefined` values.
*
* @default false
*/
removeUndefined?: boolean;
/** --------------------------------------------------
* * ***Remove empty objects `{}` from the final output, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
removeEmptyObjects?: boolean;
/** --------------------------------------------------
* * ***Remove empty arrays `[]` from the final output, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
removeEmptyArrays?: boolean;
/** --------------------------------------------------
* * ***Removes values that do not match selected conversions, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
strictMode?: boolean;
/** --------------------------------------------------
* * ***Enable error logging if JSON parsing fails, defaultValue: `false`.***
* --------------------------------------------------
*
* @default false
*/
loggingOnFail?: boolean;
/** --------------------------------------------------
* * ***Custom error handler function.***
* --------------------------------------------------
*
* - ***Behavior:***
* - If provided, it will be called with the error.
* - If not provided, defaults to `undefined` in type, but internally a no-op function is used.
*
* @param error - Error instance thrown during fail on execution.
* @default undefined
*/
onError?: (error: Error) => void;
/** --------------------------------------------------
* * ***Whether to check symbol properties when checking empty objects.***
* --------------------------------------------------
*
* @default false
*/
checkSymbols?: boolean;
};
/** --------------------------------------------------
* * ***Utility: `cleanParsedData`.***
* ---------------------------------------------
* **Cleans parsed JSON data based on provided options.**
* @template T - Expected output type.
* @param {*} data - The parsed JSON data.
* @param {ParseParsedDataOptions} [options] - The cleaning options.
* @returns {T | null | undefined} The cleaned data.
* - ***⚠️ Notice:*** _If data is JSON string, we recommend use ***`safeJsonParse` utility function*** for more safe._
* - ***⚠️ Note:*** _If using **`convertDates`** **options**, result may contain Date objects, you may need type assertions in strict TypeScript settings._
* @example
* ```ts
* // 1: Convert numbers and remove nulls
* const result = cleanParsedData({ age: "25", name: null }, {
* convertNumbers: true,
* removeNulls: true
* });
* console.log(result); // ➔ { age: 25 }
* // 2: Convert boolean strings
* const result = cleanParsedData({ isActive: "true" }, {
* convertBooleans: true
* });
* console.log(result); // ➔ { isActive: true }
* ```
*/
declare const cleanParsedData: <T = unknown>(data: T, options?: ParseParsedDataOptions) => T | undefined | null;
/** --------------------------------------------------
* * ***Utility: `parseCustomDate`.***
* ---------------------------------------------
* **Parses custom date formats like "DD/MM/YYYY" or "MM/DD/YYYY".**
* @param {string} dateString - Date string to parse.
* @param {string} format - Date format to match.
* @returns {Date | null} Returns a `Date` object if valid, otherwise `null`.
* @throws **{@link TypeError | `TypeError`}** if `dateString` **(first parameter)** and `format` **(second parameter)** is not a string or empty-string.
* @example
* // Valid: European format (DD/MM/YYYY)
* const date1 = parseCustomDate("03/09/2025", "DD/MM/YYYY");
* console.log(date1); // ➔ Date { Wed Sep 03 2025 ... }
*
* // Valid: US format (MM/DD/YYYY)
* const date2 = parseCustomDate("09/03/2025", "MM/DD/YYYY");
* console.log(date2); // ➔ Date { Wed Sep 03 2025 ... }
*
* // Invalid: wrong format
* const date3 = parseCustomDate("2025-09-03", "DD/MM/YYYY");
* console.log(date3); // ➔ null
*
* // Invalid: non-date string
* const date4 = parseCustomDate("hello", "DD/MM/YYYY");
* console.log(date4); // ➔ null
*
* // Throws: wrong parameter types or empty-string
* parseCustomDate(123, "DD/MM/YYYY");
* // ➔ TypeError: Parameter `dateString` and `format` must be of type `string`...
*/
declare const parseCustomDate: (dateString: string, format: string) => Date | null;
type NonJsonParsableType = Omit<Exclude<unknown, string | null | undefined>, string>;
type Contains<T, U> = [Extract<T, U>] extends [never] ? false : true;
/** @private ***The value type for the overload function {@link safeJsonParse | `safeJsonParse`}.*** */
type UnknownValue = {
undefined: true;
};
/** @private ***The narrows type result for {@link safeJsonParse | `safeJsonParse`}.*** */
type SafeJsonParseResult<TData, T> = IfNotExtends<T, NonJsonParsableType> extends true ? T extends never ? undefined : T extends void ? undefined : T extends number ? undefined : Contains<T, string> extends true ? Contains<T, null & string> extends true ? TData | null | undefined : TData | undefined : IfExtends<T, null> extends true ? null : IfNotExtends<T, NonJsonParsableType> extends true ? TData | null | undefined : undefined : Contains<T, string> extends true ? IsAny<T> extends true ? TData | undefined | null : TData | undefined : undefined;
/** --------------------------------------------------
* * ***Utility: `safeJsonParse`.***
* ---------------------------------------------
* **Safely parses JSON while handling errors and applying transformations.**
* - **Supports generics** to ensure accurate return type inference.
* - Always provide both `<TData, TInput>` for best results.
* - ℹ️ ***Scroll down for full generic behavior explanation.***
* - Automatically parses valid JSON strings into `objects`, `arrays`, `numbers`, etc.
* - Supports data transformation via options (e.g., convert strings to `numbers`, `booleans`, or `dates`).
* - **Returns:**
* 1. `null` ➔ if input is explicitly `null`.
* 2. `undefined` ➔ if input is `undefined`, not a `string`, or if parsing fails.
* 3. Parsed and cleaned result (`TData`) ➔ if input is a valid JSON string.
* - ⚠️ **JSON.stringify note**:
* - _If the input JSON string was created using `JSON.stringify()`, any properties with `undefined`
* values would have been automatically removed or converted to `null` depending on the serializer, example:_
* ```ts
* JSON.stringify({ a: undefined, b: 1 }); // ➔ '{"b":1}'
* JSON.parse('{"a": undefined, "b": 1}') // ❌ invalid JSON
* ```
* _Therefore, if you see `undefined` in raw input, it will likely throw unless pre-cleaned or replaced with `null`._
*
* ```ts
* safeJsonParse('{"name": "John", "score": undefined}');
* // result ➔ { name: "John", score: null } <- because `undefined` is not valid JSON, gets replaced to null.
* ```
* - ℹ️ **Additionally:**
* - This function normalizes single quotes (`'`) to double quotes (`"`) before parsing,
* so JSON strings using single quotes will be converted to valid JSON format, example:
* ```ts
* safeJsonParse("{'name': 'John', 'age': 30}");
* // result ➔ { name: "John", age: 30 }
*
* safeJsonParse("{'string\\'s': 'abc', \"quote's\": 'It\\'s awesome!', 'aNumber\\'s': 123, 'keyWith\\'Backslash\\'s': 'value\\'s'}");
* // result ➔ { "string's": "abc", "quote's": "It's awesome!", "aNumber's": 123, "keyWith'Backslash's": "value's" }
* ```
* @template TData - The expected output type after parsing and cleaning.
* @template TInput - The input value type, used for advanced type inference and return typing.
* @param {TInput} [value] - The JSON string or value to parse.
* @param {ParseParsedDataOptions} [options] - Options to clean, convert types, enable strict mode,
* support custom date formats, enable logging, or handle errors via callback.
* @returns {SafeJsonParseResult<TData, TInput>} Parsed and optionally cleaned result, or `null`/`undefined`.
* @throws **{@link TypeError | `TypeError`}** if `options` is provided but not a valid object.
* @example
* 1. ***Basic parse with number & boolean conversion:***
* ```ts
* const result = safeJsonParse(30);
* // result ➔ undefined
* const result = safeJsonParse(30, {
* convertNumbers: true
* });
* // result ➔ 30
*
* const result = safeJsonParse('{"age": "30", "isActive": "true"}', {
* convertNumbers: true,
* convertBooleans: true
* });
* // result ➔ { age: 30, isActive: true }
* ```
* 2. ***Handling `undefined` in input string (manually written, not JSON.stringify):***
* ```ts
* const result = safeJsonParse('{"score": undefined}');
* // result ➔ { score: null } <- because `undefined` is not valid JSON, gets replaced
* ```
*
* 3. ***Handling `NaN` in input string (manually written, not JSON.stringify):***
* ```ts
* const value = NaN; // <- value is NaN or "NaN";
* const result = safeJsonParse(value);
* // result ➔ undefined <- will return as undefined, because options `convertNaN` is false (default),
* const result2 = safeJsonParse(value, { convertNaN: true });
* // result2 ➔ NaN <- will return as undefined because options `convertNaN` is false,
*
* const result4 = safeJsonParse('{"strNan": "NaN", "pureNan": NaN}');
* // NaN will convert to string (NaN ➔ "NaN") because options `convertNaN` is false (default),
* // result4 ➔ { strNan: "NaN", pureNan: "NaN" }
*
* const result3 = safeJsonParse('{"strNan": "NaN", "pureNan": NaN}', {
* convertNaN: true
* });
* // String "NaN" will convert to NaN ("NaN" ➔ NaN) because options `convertNaN` is true,
* // result3 ➔ { strNan: NaN, pureNan: NaN }
* ```
*
* 4. ***Strict mode (removes invalid values):***
* ```ts
* const result = safeJsonParse('{"name": " ", "score": "99abc"}', {
* convertNumbers: true,
* strictMode: true
* });
* // result ➔ {}
*
* const result2 = safeJsonParse('{"name": " ", "score": undefined}');
* // result2 ➔ { name: "",score: null }
* ```
*
* 5. ***Custom date format parsing:***
* ```ts
* const result = safeJsonParse('{"birthday": "25/12/2000"}', {
* convertDates: true,
* customDateFormats: ["DD/MM/YYYY"]
* });
* // result ➔ { birthday: new Date("2000-12-25T00:00:00.000Z") }
* ```
*
* 6. ***Invalid JSON with custom error handling:***
* ```ts
* safeJsonParse("{invalid}", {
* loggingOnFail: true,
* onError: (err) => console.log("Custom handler:", err.message)
* });
* // ➔ Logs parsing error and invokes handler
* ```
*
* 7. ***Null or non-string input returns null/undefined (default options):***
* ```ts
* safeJsonParse(123); // ➔ undefined
* safeJsonParse(null); // ➔ null
* safeJsonParse(undefined); // ➔ undefined
* ```
*
* 8. ***Generic usage: Provide both output and input type to ensure correct return typing:***
* ```ts
* type UserType = { name: string };
*
* const obj = JSON.stringify({
* name: "John"
* });
*
* const toParse = isAuth() ? obj : null;
* const toParse2 = isAuth() ? obj : undefined;
*
* // * `Without Generic`:
* const parsed = safeJsonParse(toParse);
* //- runtime: { name: "John" } | undefined | null
* //- type: Record<string, unknown> | undefined | null
* const parsed2 = safeJsonParse(toParse);
* //- runtime: { name: "John" } | undefined
* //- type: Record<string, unknown> | undefined
*
* // * `With Generic`:
* const parsed = safeJsonParse<UserType>(toParse);
* //- runtime: { name: "John" } | undefined | null
* //- type: undefined <- (⚠️ unexpected!)
* const parsed2 = safeJsonParse<UserType>(toParse);
* //- runtime: { name: "John" } | undefined
* //- type: undefined <- (⚠️ unexpected!)
* const parsed = safeJsonParse<UserType, typeof toParse>(toParse);
* //- runtime: { name: "John" } | null | undefined
* //- type: UserType | null | undefined
* const parsed2 = safeJsonParse<UserType, typeof toParse>(toParse);
* //- runtime: { name: "John" } | undefined
* //- type: UserType | undefined
* ```
* @note
* ⚠️ **Generic Behavior:**
* - This function supports advanced generic inference for clean, type-safe return values.
* - If only the first generic (`TData`) is provided and the second (`TInput`) is omitted,
* then `TInput` defaults to `undefined`, resulting in a return type of `undefined`.
* - To ensure correct return typing, **always pass both generics** when `value` is dynamic,
* nullable, or unioned: `safeJsonParse<TData, typeof value>(value)`.
* - This makes the returned type exactly match your expectation: `TData | null | undefined`.
*/
declare function safeJsonParse<TData extends Record<string, any> = Record<string, unknown>, TInput extends UnknownValue = UnknownValue>(value: TInput, options?: ParseParsedDataOptions): IsAny<TInput> extends true ? TData | null | undefined : undefined;
declare function safeJsonParse<TData extends Record<string, any> = Record<string, unknown>, TInput extends string | null | undefined | unknown = undefined>(value: TInput, options?: ParseParsedDataOptions): SafeJsonParseResult<TData, TInput>;
/** ----------------------------------------------------------
* * ***Utility: `extractDigits`.***
* ---------------------------------------------
* **Extracts digits from a string or number input.**
* - **Behavior:**
* - Converts the input to a string, trims whitespace, and removes any characters that are not digits (`0-9`).
* - Returns the cleaned numeric value as a `number`.
* - If the input is a `null`, `undefined`, results in no digits, or not a `string` (or empty-string) or `number`, it safely return `0`.
* @param {*} [value]
* **The value to process.**
* - Accepts a string, number, `null`, or `undefined`.
* @returns {number} The numeric value after extracting digits (returns `0` if input is invalid or contains no digits).
* @example
* extractDigits(12345); // ➔ 12345
* extractDigits("9A8B7C6X1"); // ➔ 98761
* extractDigits("123abc456"); // ➔ 1