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,123 lines (1,118 loc) 96.8 kB
/*! * ==================================================== * 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 ➔ `""`