@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,123 lines (1,118 loc) • 96.8 kB
TypeScript
/*!
* ====================================================
* Rzl Utils-JS.
* ----------------------------------------------------
* Version: 3.11.0.
* Author: Rizalvin Dwiky.
* Repository: https://github.com/rzl-zone/utils-js.
* ====================================================
*/
import { NumberFormat, CountryCode } from 'libphonenumber-js';
import { OverrideTypes, ExtractStrict, Prettify, OmitStrict, AnyString } from '@rzl-zone/ts-types-plus';
import { FormatOptions, Locale } from 'date-fns';
type NegativeFormatOptionCustom = {
/** Custom formatter function for the final formatted negative string.
* If provided, it ***OVERRIDES*** style & space entirely. */
custom: (formatted: string) => string;
style?: never;
space?: never;
};
type NegativeFormatOptionUnCustom = {
custom?: never;
/** Use style & optional spacing for negative numbers.
*
* @default "dash"
*/
style?: "dash" | "brackets" | "abs";
/** Whether to include space inside brackets or after dash.
*
* Default: false
* @default false
*/
space?: boolean;
};
/** ---------------------------------------------------------------------------
* * ***Type for negative number formatting options.***
* ---------------------------------------------------------------------------
*/
type NegativeFormatOption = "dash" | "brackets" | "abs" | NegativeFormatOptionCustom | NegativeFormatOptionUnCustom;
/** ---------------------------------------------------------------------------
* * ***Type Options for {@link formatCurrency|`formatCurrency`}.***
* ---------------------------------------------------------------------------
*/
type FormatCurrencyOptions = {
/** ---------------------------------------------------------------------------
* * ***Prefix currency string.***
* ---------------------------------------------------------------------------
* **Does not auto-keep input symbols.**
* - ***DefaultValue:** `""`.*
* - **Example:** `"Rp "` ➔ `Rp 1.000`.
*
* @default ""
*/
suffixCurrency?: string;
/** ---------------------------------------------------------------------------
* * ***Thousands separator.***
* ---------------------------------------------------------------------------
* - ***DefaultValue:** `"."`.*
* - **Example:** `"."` ➔ `1.000.000`.
* @default "."
*/
separator?: string;
/** ---------------------------------------------------------------------------
* * ***Prefix currency string.***
* ---------------------------------------------------------------------------
* **Whether to show decimals, if `false`, decimals are truncated.**
* - ***DefaultValue:** `false`.*
* @default false
*/
decimal?: boolean;
/** ---------------------------------------------------------------------------
* * ***Total decimal digits.***
* ---------------------------------------------------------------------------
* **If `decimal: true` & `roundedDecimal: false`, simply cuts.**
* - ***DefaultValue:** `2`.*
* @default 2
*/
totalDecimal?: number;
/** ---------------------------------------------------------------------------
* * ***Actually append `suffixDecimal`.***
* ---------------------------------------------------------------------------
* - ***DefaultValue:** `true`.*
* @default true
*/
endDecimal?: boolean;
/** ---------------------------------------------------------------------------
* * ***Text appended after decimals.***
* ---------------------------------------------------------------------------
* - ***DefaultValue:** `""`.*
* - **Example:**
* - `".-"` ➔ `1.000,00.-`.
* - `" USD"` ➔ `1.000,00 USD`.
* @default ""
*/
suffixDecimal?: string;
/** ---------------------------------------------------------------------------
* * ***Rounding mode for decimals.***
* ---------------------------------------------------------------------------
* - **Behavior:**
* - `"round"` ➔ nearest.
* - `"ceil"` ➔ always up.
* - `"floor"` ➔ always down.
* - `false` ➔ truncate.
* - ***DefaultValue:** `"round"`.*
* @default "round"
*/
roundedDecimal?: "round" | "ceil" | "floor" | false;
/** ---------------------------------------------------------------------------
* * ***Decimal separator.***
* ---------------------------------------------------------------------------
* - ***DefaultValue:** `","`.*
* - **Example:** `","` ➔ `1.000,10`.
* @default ","
*/
separatorDecimals?: string;
/** ---------------------------------------------------------------------------
* * ***Negative formatting option.***
* ---------------------------------------------------------------------------
* **Can be string ("dash" | "brackets" | "abs") or object with custom formatter.**
* - **Behavior:**
* - `"dash"` ➔ `-15.000`.
* - `"brackets"` ➔ `(15.000)`.
* - `"abs"` ➔ `15.000` (always positive).
* - Or object:
* `{
* style: "dash"|"brackets"|"abs",
* space: true|false,
* custom: (formatted) => string
* }`.
* - ***DefaultValue:** `"dash"`.*
*
* @default "dash"
*/
negativeFormat?: NegativeFormatOption;
/** ---------------------------------------------------------------------------
* * ***Applies Indian Format.***
* ---------------------------------------------------------------------------
* - **Behavior:**
* - If `true`, formats as Indian: `12,34,567`.
* - Also forces `separator: ","`, `separatorDecimals: "."`.
* - ***DefaultValue:** `false`.*
* @default false
*/
indianFormat?: boolean;
};
/** -------------------------------------------------------
* * ***Utility: `formatCurrency`.***
* -------------------------------------------------------
* **Formats a number or messy currency string into a
* beautifully formatted currency string, with highly
* customizable separators, decimal control, rounding,
* currency symbols, and negative styling.**
* - **✅ Highlights:**
* - ***Accepts:***
* - Pure numbers: `15300.95`.
* - Messy currency strings from **any locale**:
* - `"Rp 15.000,21"` ***(Indonesian / Euro)***.
* - `"$12,345.60"` ***(US)***.
* - `"CHF 12'345.60"` ***(Swiss)***.
* - `"1,23,456.78"` ***(Indian)***.
* - Auto extracts numeric value with smart multi-locale parsing
* via ***`parseCurrencyString` utility function***.
* - Strong type checks & clear errors for misconfigured options.
* - **Handles:**
* - Thousands separators: `.`, `,`, `'`, ` `.
* - Decimal separators: `,`, `.`.
* - Decimal suffix (eg. `".-"`, `" USD"`).
* - Currency prefix (eg. `"Rp "`, `"$ "`).
* - Rounding: `"round"`, `"ceil"`, `"floor"`, or truncate.
* - Negative styling: dash `-`, brackets `( )`, absolute, or custom.
* - **✅ How input is parsed:**
* - Removes all non-digit except `.`, `,`, `'` and spaces.
* - Detects bracket negatives: `"(15.000,10)"` ➔ `-15000.10`.
* - Uses last `,` or `.` as decimal separator (others are thousands).
* - Ignores currency symbols like `Rp`, `$` (must re-apply with `suffixCurrency`).
* - **ℹ️ Note:**
* - Always re-apply symbols via `suffixCurrency`.
* - `parseCurrencyString` smartly detects last decimal,
* so `"1.121.234,56"` and `"1,121,234.56"` both parsed correctly.
* @param {string|number} value
* ***The input value to format, examples:***
* - `"Rp 15.000,21"`.
* - `"$12,345.60"`.
* - `"CHF 12'345.60"`.
* - `15300.95`.
* @param {FormatCurrencyOptions} [options] ***Optional configuration object.***
* @param {FormatCurrencyOptions["separator"]} [options.separator]
* ***Thousands separator:***
* - `{ separator: "." }` ➔ `1.000.000`.
* - *DefaultValue: `"."`.*
* @param {FormatCurrencyOptions["separatorDecimals"]} [options.separatorDecimals]
* ***Decimal separator:***
* - `{ separatorDecimals: "," }` ➔ `1.000,10`.
* - *DefaultValue: `","`.*
* @param {FormatCurrencyOptions["suffixCurrency"]} [options.suffixCurrency]
* ***Prefix currency string:***
* - Does **not auto-keep input symbols**.
* - Must set manually e.g: `{ suffixCurrency: "Rp " }`.
* - `{ suffixCurrency: "Rp " }` ➔ `Rp 1.000`.
* - *DefaultValue: `""`.*
* @param {FormatCurrencyOptions["decimal"]} [options.decimal]
* ***Whether to show decimals. If `false`, decimals are truncated:***
* - If `false`, cut the decimal.
* - *DefaultValue: `false`.*
* @param {FormatCurrencyOptions["totalDecimal"]} [options.totalDecimal]
* ***Total decimal digits:***
* - If `decimal: true` & `roundedDecimal: false`, simply cuts.
* - *DefaultValue: `2`.*
* @param {FormatCurrencyOptions["separatorDecimals"]} [options.suffixDecimal]
* ***Text appended after decimals:***
* - E.g: (`".-"`, `" USD"`).
* - Example 1: `".-"` ➔ `1.000,00.-`.
* - Example 2: `" USD"` ➔ `1.000,00 USD`.
* - *DefaultValue: `""`.*
* @param {FormatCurrencyOptions["endDecimal"]} [options.endDecimal]
* ***Actually append `suffixDecimal`:***
* - *DefaultValue: `true`.*
* @param {FormatCurrencyOptions["roundedDecimal"]} [options.roundedDecimal]
* ***Rounding mode:***
* - `"round"` ➔ nearest.
* - `"ceil"` ➔ always up.
* - `"floor"` ➔ always down.
* - `false` ➔ truncate.
* - *DefaultValue: `"round"`.*
* @param {FormatCurrencyOptions["negativeFormat"]} [options.negativeFormat]
* ***How to format negatives:***
* - `"dash"` ➔ `-15.000`.
* - `"brackets"` ➔ `(15.000)`.
* - `"abs"` ➔ `15.000` (always positive).
* - Or object: `{ style: "dash" | "brackets" | "abs", space: true | false, custom: (formatted) => string }`.
* - *DefaultValue: `"dash"`.*
* @param {FormatCurrencyOptions["indianFormat"]} [options.indianFormat]
* ***Applies Indian Format:***
* - If `true`, formats as Indian: `12,34,567`.
* - Also forces `separator: ","`, `separatorDecimals: "."`.
* @returns {string}
* ***Nicely formatted currency string, examples:***
* - `"15.000,10"`.
* - `"Rp 15.000,10.-"`.
* - `"15'000.10 USD"`.
* - `"12,34,567.89"`.
* @throws **{@link TypeError | `TypeError`}** ***If:***
* - The `value` is not string or number.
* - Cannot parse to valid number.
* - Options have invalid types.
* @example
* // --- Number input (default, decimals off) ---
* formatCurrency(1234567.89);
* // ➔ "1.234.567"
*
* // --- Decimals enabled ---
* formatCurrency(1234567.89, { decimal: true });
* // ➔ "1.234.567,89"
*
* // --- Indian format ---
* formatCurrency(1234567.89, { decimal: true, indianFormat: true });
* // ➔ "12,34,567.89"
*
* // --- String input (Indonesian style) ---
* formatCurrency("Rp 15.000,21", { decimal: true });
* // ➔ "15.000,21"
*
* // --- String input (US style) ---
* formatCurrency("$12,345.60", { decimal: true });
* // ➔ "12.345,60"
*
* // --- String input (Swiss style) ---
* formatCurrency("CHF 12'345.60", { decimal: true });
* // ➔ "12'345,60"
*
* // --- String input (Indian style) ---
* formatCurrency("1,23,456.78", { decimal: true, indianFormat: true });
* // ➔ "12,34,567.78"
*
* // --- Negative numbers (dash) ---
* formatCurrency(-1234.56, { decimal: true });
* // ➔ "-1.234,56"
*
* // --- Negative numbers (brackets) ---
* formatCurrency(-1234.56, {
* decimal: true,
* negativeFormat: "brackets"
* });
* // ➔ "(1.234,56)"
*
* // --- Negative numbers (custom object style) ---
* formatCurrency(-1234.56, {
* decimal: true,
* negativeFormat: { style: "brackets", space: true }
* });
* // ➔ "( 1.234,56 )"
*
* // --- Negative numbers (custom function) ---
* formatCurrency(-1234.56, {
* decimal: true,
* negativeFormat: { custom: (val) => `NEGATIVE[${val}]` }
* });
* // ➔ "NEGATIVE[1.234,56]"
*
* // --- With prefix currency ---
* formatCurrency(1234.56, {
* decimal: true,
* suffixCurrency: "Rp "
* });
* // ➔ "Rp 1.234,56"
*
* // --- With suffix decimal ---
* formatCurrency(1234.56, {
* decimal: true,
* suffixDecimal: ".-"
* });
* // ➔ "1.234,56.-"
*
* // --- With suffix currency + suffix decimal ---
* formatCurrency(1234.56, {
* decimal: true,
* suffixCurrency: "Rp ",
* suffixDecimal: ".-"
* });
* // ➔ "Rp 1.234,56.-"
*
* // --- Custom separators ---
* formatCurrency(1234567.89, {
* decimal: true,
* separator: "'",
* separatorDecimals: "."
* });
* // ➔ "1'234'567.89"
*
* // --- Rounding: ceil ---
* formatCurrency(1234.561, {
* decimal: true,
* roundedDecimal: "ceil"
* });
* // ➔ "1.234,57"
*
* // --- Rounding: floor ---
* formatCurrency(1234.569, {
* decimal: true,
* roundedDecimal: "floor"
* });
* // ➔ "1.234,56"
*
* // --- Rounding: truncate (false) ---
* formatCurrency(1234.569, {
* decimal: true,
* roundedDecimal: false
* });
* // ➔ "1.234,56"
*
* // --- Force no decimals (decimal: false) ---
* formatCurrency(1234.567, { decimal: false });
* // ➔ "1.235"
*
* // --- Edge case: messy input with dots & commas ---
* formatCurrency("1.121.234,561", {
* decimal: true,
* totalDecimal: 2,
* roundedDecimal: "ceil",
* suffixCurrency: "Rp ",
* negativeFormat: { style: "brackets" }
* });
* // ➔ "(Rp 1.121.234,57)"
*
* // --- Edge case: integer string input ---
* formatCurrency("1.121.234", {
* decimal: true,
* suffixCurrency: "Rp ",
* roundedDecimal: "ceil"
* });
* // ➔ "Rp 1.121.234,00"
*/
declare const formatCurrency: (value: string | number, options?: FormatCurrencyOptions) => string;
/** ----------------------------------------------------------
* * ***Utility: `formatNumber`.***
* ----------------------------------------------------------
* **Formats a number or numeric string by adding a custom separator
* every three digits (thousands separator), and intelligently flips
* the decimal separator according to the chosen separator.**
* - **Features:**
* - Converts a number to string before formatting.
* - Defaults to using `,` as the thousands separator.
* - If `.` is used as the separator, the decimal will automatically
* become `,`, and vice versa.
* - Handles input with existing formatting (e.g. "1,234,567.89") and normalizes it.
* - Supports custom separators, including spaces.
* - Preserves decimals even if more than 2 digits.
* @param {string | number} value - The numeric value or string to format, can be plain numbers, or already formatted strings like `"1,234,567.89"`.
* @param {string} [separator=","] - The thousands separator to use, examples: `","` ***(default)***, `"."`, `" "`, etc.
* @returns {string} The formatted string with thousands separators and
* appropriate decimal separator.
* @throws **{@link TypeError | `TypeError`}** if `value` is not a string or number, or `separator` is not a string.
* @example
* formatNumber(1000000);
* // ➔ "1,000,000"
* formatNumber("987654321");
* // ➔ "987,654,321"
* formatNumber(1234567.89);
* // ➔ "1,234,567.89"
* formatNumber("1234567,89");
* // ➔ "1,234,567.89"
* formatNumber("1234567.892");
* // ➔ "1,234,567.892"
* formatNumber("1234567.89", ".");
* // ➔ "1.234.567,89"
* formatNumber("1234567,89", ",");
* // ➔ "1,234,567.89"
* formatNumber("987654321", " ");
* // ➔ "987 654 321"
* formatNumber("1,234,567.89");
* // ➔ "1,234,567.89"
* formatNumber("1.234.567,89", ",");
* // ➔ "1,234,567.89"
* formatNumber("1.234.567,893", ".");
* // ➔ "1.234.567,893"
* formatNumber("1234.56", ".");
* // ➔ "1.234,56"
* formatNumber("1234,56", ",");
* // ➔ "1,234.56"
*/
declare const formatNumber: (value: string | number, separator?: string) => string;
/** -------------------------------------------------------
* * ***Output format mode for {@link formatPhoneNumber|`formatPhoneNumber`}.***
* -------------------------------------------------------
* - `'E.164'` ➔ `+6281234567890`
* - `'RFC3966'` ➔ `tel:+62-812-3456-7890`
* - `'NATIONAL'` ➔ `0812 3456 7890`
* - `'INTERNATIONAL'` ➔ `+62 812 3456 7890`
*/
type OutputFormat = ExtractStrict<NumberFormat, "INTERNATIONAL" | "NATIONAL" | "RFC3966" | "E.164">;
/** -------------------------------------------------------
* * ***Single input value for {@link formatPhoneNumber|`formatPhoneNumber`}.***
* -------------------------------------------------------
* - **Accepts:**
* - `string` — e.g. `"0812 3456 7890"`
* - `number` — e.g. `81234567890`
* - `null` or `undefined` — represents no input
* - **ℹ️ Notes**
* - The function normalizes all **non-digit characters** (spaces, dots, dashes,
* parentheses, etc.) before validation/formatting.
* - When you pass a `number`, any **leading zeros are lost by JavaScript**.
* - Prefer using a `string` if the number may begin with `0`.
* - E.164 international standard allows **up to 15 digits** (not counting `+`).
*/
type ValueFormatPhoneNumber = string | number | null | undefined;
/** -------------------------------------------------------
* * ***Base option set for {@link formatPhoneNumber|`formatPhoneNumber`}.***
* -------------------------------------------------------
* **All properties are optional.**
* @description
* Defaults apply when a property is omitted or `undefined`.
*
* **⚠️ Overload-aware notes:**
* - If `checkValidOnly` is `true`, **all other properties are ignored**.
* - If `takeNumberOnly` is `true`, **all formatting properties are ignored**.
* - The leading `+` is **recommended** but not required;
* the regex will still validate numbers without `+`
* as long as the digit count ≤ **15**.
*/
type FormatPhoneNumberMain = {
/** -------------------------------------------------------
* * ***Separator for formatted output.***
* -------------------------------------------------------
* **Defines the string used to separate groups of digits**
* in the formatted phone number.
* - **Default:** `" "`.
* - **Executed only when:**
* - Parameter options `checkValidOnly` and `takeNumberOnly` are both `false`.
* - (This option is ignored if either `checkValidOnly` or `takeNumberOnly` is `true`.)
* - **Behavior:**
* - The formatter inserts this separator between number blocks
* according to the selected `outputFormat`.
* @default " "
* @example
* ```ts
* // Using dash as separator
* formatPhoneNumber("081234567890", { defaultCountry: "ID", separator: "-" });
* // ➔ "+62 812-3456-7890"
*
* // Using space as separator
* formatPhoneNumber("(151) 2345-6789", { defaultCountry: "DE", separator: " " });
* // ➔ "+49 1512 3456789"
* ```
*/
separator?: string;
/** -------------------------------------------------------
* * ***Output format style for the returned phone number.***
* -------------------------------------------------------
* **Determines how the formatted phone number string is returned.**
*
* - **Default:** `"INTERNATIONAL"`.
* - **Applicable only when:**
* - Parameter options `checkValidOnly` and `takeNumberOnly`
* are both **`false`**.
* - (Ignored if either of those options is `true`.)
*
* - **Supported values (from {@link NumberFormat}):**
* - `"NATIONAL"` – Local/national format, e.g. `0812 3456 7890`.
* - `"INTERNATIONAL"` – International format with leading plus, e.g. `+62 812 3456 7890`.
* - `"E.164"` – Compact E.164 format, e.g. `+6281234567890`.
* - `"RFC3966"` – RFC 3966 URI format, e.g. `tel:+62-812-3456-7890`.
*
* @default "INTERNATIONAL"
* @example
* ```ts
* // Returns a national-format string
* formatPhoneNumber("+62 81234567890", { outputFormat: "NATIONAL" });
* // ➔ "0812 3456 7890"
*
* // Returns an E.164-format string
* formatPhoneNumber("+62 81234567890", { outputFormat: "E.164" });
* // ➔ "+6281234567890"
* ```
*/
outputFormat?: OutputFormat;
/** -------------------------------------------------------
* * ***Prepend a plus sign and country calling code.***
* -------------------------------------------------------
* **Forces the returned phone number to start with a leading `+`
* followed by the detected country calling code (e.g. `+63`, `+1`).**
* - **Default:** `true`.
* - **Executed only when:**
* - Parameter options `outputFormat` is set to `"INTERNATIONAL"`.
* - (This option is ignored for `"NATIONAL"`, `"E.164"` or `"RFC3966"` formats.).
* - **Applicable when:**
* - You want to guarantee that the result
* always contains a plus sign and country code, regardless of
* the selected `outputFormat`.
* - **Behavior:**
* - When `true`, the formatter ensures the output begins with
* a `+` and the correct country code.
* - When `false`, the output follows the chosen `outputFormat`
* without forcing a `+` prefix.
* @default true
* @example
* ```ts
* // Automatically adds +63 (default: `true`) even if input is local format
* formatPhoneNumber("09171234567", {
* country: "PH",
* prependPlusCountryCode: true
* });
* // ➔ "+63 917 123 4567"
*
* formatPhoneNumber("09171234567", {
* country: "PH",
* prependPlusCountryCode: false
* });
* // ➔ "63 917 123 4567"
*
* // Leaves number in national format (no plus sign)
* formatPhoneNumber("+63 9171234567", {
* country: "PH",
* prependPlusCountryCode: false,
* outputFormat: "NATIONAL"
* });
* // ➔ "0917 123 4567"
* ```
*/
prependPlusCountryCode?: boolean;
/** -------------------------------------------------------
* * ***Characters before the country code (e.g. `"("`).***
* -------------------------------------------------------
* **Adds a custom string that appears **immediately before** the
* international country calling code when formatting.**
* - **Default:** `""` (empty string).
* - **Behavior:**
* - **Active only when:**
* - `checkValidOnly` is **false**,
* - `takeNumberOnly` is **false**, **and**
* - `outputFormat` is `"INTERNATIONAL"`.
* - **Ignored if:**
* - The value is an empty string (after trimming),
* - `checkValidOnly` or `takeNumberOnly` is `true`,
* - `outputFormat` is not `"INTERNATIONAL"`,
* - `closingNumberCountry` is `undefined` or an empty string (after trimming).
* - **Invalid input:**
* - Returns no effect if the phone number is invalid or not compatible
* with the selected `defaultCountry`.
* @default ""
* @example
* ```ts
* formatPhoneNumber("+63 9171234567", {
* outputFormat: "INTERNATIONAL",
* openingNumberCountry: "(",
* closingNumberCountry: ")"
* });
* // ➔ "(+63) 917 123 4567"
* ```
*/
openingNumberCountry?: string;
/** -------------------------------------------------------
* * ***Characters after the country code (e.g. `")"`).***
* -------------------------------------------------------
* **Adds a custom string that appears **immediately after** the
* international country calling code when formatting.**
* - **Default:** `""` (empty string).
* - **Behavior:**
* - **Active only when:**
* - `checkValidOnly` is **false**,
* - `takeNumberOnly` is **false**, **and**
* - `outputFormat` is `"INTERNATIONAL"`.
* - **Ignored if:**
* - The value is an empty string (after trimming),
* - `checkValidOnly` or `takeNumberOnly` is `true`,
* - `outputFormat` is not `"INTERNATIONAL"`,
* - `openingNumberCountry` is `undefined` or an empty string (after trimming).
* - **Invalid input:**
* Returns no effect if the phone number is invalid or not compatible
* with the selected `defaultCountry`.
* @default ""
* @example
* ```ts
* formatPhoneNumber("+63 9171234567", {
* outputFormat: "INTERNATIONAL",
* openingNumberCountry: "(",
* closingNumberCountry: ")"
* });
* // ➔ "(+63) 917 123 4567"
* ```
*/
closingNumberCountry?: string;
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
* @example
* ```ts
* // Returns `true` if valid number and number with country code (no need `defaultCountry`)
* formatPhoneNumber("+63 912-123-4567", { checkValidOnly: true });
* // ➔ true
*
* // Returns `true` if valid number and number without country code but with `defaultCountry`
* formatPhoneNumber("213-373-4253", { defaultCountry: "US", checkValidOnly: true });
* // ➔ true
*
* // Returns `false` if without country code.
* formatPhoneNumber("213-373-4253", { checkValidOnly: true });
* // ➔ false
*
* // Returns `false` for invalid number.
* formatPhoneNumber("abcd", { checkValidOnly: true });
* // ➔ false
* ```
*/
checkValidOnly?: boolean;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
* @example
* ```ts
* // Returns only digits of the local number with country code (no need `defaultCountry`)
* formatPhoneNumber("+63 912-123-4567", { takeNumberOnly: true });
* // ➔ "09121234567"
*
* // Returns only digits of the local number without country code but with `defaultCountry`
* formatPhoneNumber("213-373-4253", { defaultCountry: "US", takeNumberOnly: true });
* // ➔ "2133734253"
*
* // Returns empty string if without country code.
* formatPhoneNumber("213-373-4253", { takeNumberOnly: true });
* // ➔ ""
*
* // Returns empty string for invalid number.
* formatPhoneNumber("abcd", { takeNumberOnly: true });
* // ➔ ""
* ```
*/
takeNumberOnly?: boolean;
/** -------------------------------------------------------
* * ***A "country code" is a two-letter ISO ([`ISO-3166-1 alpha-2`](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2#Officially_assigned_code_elements)) country code (like `"US"` | `"ID"` | `"DE"`).***
* -------------------------------------------------------
* **Used to interpret numbers without an explicit `+<countryCode>`.**
* - ***Behavior:***
* - Required if the input without country code (`+`).
* - Ignored if the input already starts with `+`.
* - ***Examples:***
* - `"ID"` ➔ Indonesian.
* - `"US"` ➔ United States.
* - `"GB"` ➔ United Kingdom.
* - ***DefaultValue: `undefined`***.
* @example
* formatPhoneNumber("081234567890", { defaultCountry: "ID" });
* @default undefined
*/
defaultCountry?: CountryCode;
};
/** -------------------------------------------------------
* * ***Specialized options for the `transformPhoneNumber` variant of {@link formatPhoneNumber|`formatPhoneNumber`}.***
* -------------------------------------------------------
* **Ensures that `checkValidOnly` and `takeNumberOnly` are both
* forced to `false` when transforming/formatting.**
*
* This type is intended for scenarios where you **must** receive a formatted
* string as output and never a `boolean` or digit-only result.
*
* **Example Output:** `+62 812 3456 7890`
*/
type FormatPhoneNumberTransform = OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
* @requires `false` or `undefined`
*/
checkValidOnly?: never;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
* @requires `false` or `undefined`
*/
takeNumberOnly?: never;
}>;
type NeverForRestFormatPhoneNumberTransform = {
/** -------------------------------------------------------
* * ***Not used in this mode **`(Never allowed in this mode)`**.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options must be omitted or are ignored.
* - Conflicts with `takeNumberOnly` and `checkValidOnly`:
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* @requires `undefined`
*/
separator?: never;
/** -------------------------------------------------------
* * ***Not used in this mode **`(Never allowed in this mode)`**.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options must be omitted or are ignored.
* - Conflicts with `takeNumberOnly` and `checkValidOnly`:
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
*
* @requires `undefined`
*/
openingNumberCountry?: never;
/** -------------------------------------------------------
* * ***Not used in this mode **`(Never allowed in this mode)`**.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options must be omitted or are ignored.
* - Conflicts with `takeNumberOnly` and `checkValidOnly`:
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
*
* @requires `undefined`
*/
closingNumberCountry?: never;
};
/** -------------------------------------------------------
* * ***Options subset for **validity-check mode** of
* {@link formatPhoneNumber|`formatPhoneNumber`}.***
* -------------------------------------------------------
* Only `checkValidOnly` is allowed.
* All formatting-related properties are **intentionally disallowed**
* to avoid mixing validation with formatting.
*
* **Example Usage:**
* ```ts
* formatPhoneNumber("+6281234567890", { checkValidOnly: true }) // boolean
* ```
*/
type FormatPhoneNumberCheckValidOnly = Prettify<OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
*/
checkValidOnly: true;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
* @requires `false` or `undefined`
*/
takeNumberOnly?: false;
} & NeverForRestFormatPhoneNumberTransform>>;
/** -------------------------------------------------------
* * ***Options subset for calling {@link formatPhoneNumber|`formatPhoneNumber`} in
* **digits-only mode**.***
* -------------------------------------------------------
* **Only `takeNumberOnly` is allowed; all other formatting options are
* intentionally disallowed.**
*
* Use this when you want a pure numeric string without any separators or country
* decorations, but still want the function to normalize the input.
*
* **Example Output:** `"6281234567890"`
*/
type FormatPhoneNumberTakeNumberOnly = Prettify<OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
* @requires `false` or `undefined`
*/
checkValidOnly?: false;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
*/
takeNumberOnly: true;
} & NeverForRestFormatPhoneNumberTransform>>;
/** -------------------------------------------------------
* * ***Options subset for calling {@link formatPhoneNumber|`formatPhoneNumber`} force to **Validity-check Mode**.***
* -------------------------------------------------------
*/
type FormatPhoneNumberAllPassing = OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
*/
checkValidOnly: true;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
* @requires `false` or `undefined`
*/
takeNumberOnly: true;
}>;
/** -------------------------------------------------------
* * ***Options subset for calling {@link formatPhoneNumber|`formatPhoneNumber`} force to **Validity-check Mode**.***
* -------------------------------------------------------
*/
type FormatPhoneNumberAllPassingValidOnly = OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
*/
checkValidOnly: true;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`
* @default false
* @requires `false` or `undefined`
*/
takeNumberOnly?: false;
}>;
/** -------------------------------------------------------
* * ***Options subset for calling {@link formatPhoneNumber|`formatPhoneNumber`} force to **Digits-only Mode**.***
* -------------------------------------------------------
*/
type FormatPhoneNumberAllPassingTakeOnly = OverrideTypes<FormatPhoneNumberMain, {
/** -------------------------------------------------------
* * ***Return only a boolean validity flag.***
* -------------------------------------------------------
* - ***Behavior:***
* - **Exclusive mode:**
* - ⚠️ When `true`, all formatting options and `takeNumberOnly` must be omitted or are ignored.
* - Conflicts with `takeNumberOnly`:
* - ⚠️ When `checkValidOnly` is `true` and all formatting options and `takeNumberOnly` must be
* omitted or are ignored.
* - But if mistake passing props:
* - ⚠️ When `checkValidOnly` is `true` and other of formatting options was passing:
* - If `takeNumberOnly` is `true` or `false`:
* - Will return a `boolean` because `checkValidOnly` is prioritize first.
* - Output:
* - Boolean ➔ (`true` or `false`).
* - ***DefaultValue: false***
* @default false
* @requires `false` or `undefined`
*/
checkValidOnly?: false;
/** -------------------------------------------------------
* * ***Return only the digits of the phone number (local number only).***
* -------------------------------------------------------
* **Returns a string containing only numeric characters** from the **local number**,
* ignoring any country code, spaces, plus signs, or separators.
* - **Default:** `false`
* - **Behavior:**
* - **Exclusive mode:**
* - ⚠️ When set to `true`, all formatting options
* (`outputFormat`, `prependPlusCountryCode`, etc.)
* and `checkValidOnly` **must be omitted** or will be **ignored**.
* - **Conflict handling with `checkValidOnly`:**
* - If both `takeNumberOnly` and `checkValidOnly` are `true`,
* `checkValidOnly` takes priority and the function
* returns a `boolean`.
* - If `checkValidOnly` is `false` (or not provided),
* and `takeNumberOnly` is `true`,
* the function returns a **numeric string of the local number**.
* - **Invalid input:**
* - If the input is invalid or cannot be parsed
* (e.g. not matching the `defaultCountry`),
* the function returns an **empty string** (`""`).
* - **Output example:**
* - Valid input ➔ `"81234567890"` // country code removed
* - Invalid input ➔ `""`