Skip to content

validatePhone

Normalizes and validates a phone number. It returns the original input string when valid and undefined when invalid.

This helper is:

  • Pure and side‑effect‑free.
  • Intention‑revealing.
  • Explicit about strict vs loose behavior.
  • Designed for predictable, real‑world phone validation.

Use this helper when you need a safe, conservative, and well‑defined phone validator that aligns with Jane’s clarity‑first philosophy.

Function Signature

validatePhone(value: unknown, strict?: boolean): string | undefined
Name Data type Description
value unknown The phone number to validate.
strict boolean Specifies whether to enforce strict mode for validation.

Normalization

  • Uses normalizeString:
  • Only native strings are accepted.
  • Leading/trailing whitespace is trimmed.
  • Empty strings are invalid.
  • No coercion is performed.
  • Normalization is used only for validation.
  • The original input string is returned on success.

Strict Mode (E.164)

Strict mode enforces the E.164 international standard: +15551234567.

Rules

  • Must start with +.
  • Must contain only digits after the plus.
  • Must contain 8–15 digits total.
  • No spaces, hyphens, parentheses, periods, extensions, or letters.

Strict mode is designed for

  • Database storage.
  • API payloads.
  • Identity systems.
  • Normalized phone fields.

If the input does not match the E.164 pattern, the function returns undefined.

Loose Mode (Common Global Formats)

Loose mode accepts a broad set of real‑world phone formats while preserving safety and predictability.

Allowed

  • Digits.
  • Spaces, hyphens, periods, parentheses, leading +, and optional extension (x123 or ext123).
  • Case‑insensitive.
  • 1–6 digits.

Rejected

  • Letters outside a valid extension.
  • Multiple plus signs.
  • Plus sign not at the start.
  • Digit count less than 7 or greater than 15.
  • Invalid extension formats.
  • Empty digit sequences.

Digit Count Rule

Loose mode extracts all digits and ensures: 7 ≤ digit count ≤ 15.

This ensures the number is globally plausible without enforcing a specific national format.

Extension Rules (Loose Mode Only)

Extensions are allowed only at the end of the number.

Valid examples:

  • 555-123-4567 x123
  • 555-123-4567 ext123
  • +1 (555) 123-4567 x1

Invalid examples:

  • 555-123-4567 x
  • 555-123-4567 ext
  • 555-123-4567 abc
  • 555x1234567

Letters are allowed only when they match the extension pattern: (x|ext)\s?\d{1,6}.

Return Value

  • Returns the original input string when valid.
  • Returns undefined when invalid.

This preserves user‑facing fidelity and avoids accidental mutation.

Error Handling

This helper:

  • Never throws.
  • Never coerces.
  • Never mutates input.
  • Returns undefined for all invalid cases.

This aligns with Jane’s philosophy of predictable, side‑effect‑free helpers.

Design Rationale

Why strict vs loose?

Phone numbers, like emails, have:

  • A strict, canonical form (E.164).
  • A wide range of real‑world formats.

Strict mode enforces canonical storage. Loose mode accepts user input without compromising safety.

Why digit‑count validation?

Even loose mode must reject:

  • Too-short numbers
  • Too-long numbers
  • Numbers that cannot be valid globally

Digit‑count validation ensures global plausibility without enforcing national numbering plans.

Why extension‑specific letter rules?

Letters are invalid in phone numbers except in extensions.

This rule prevents false positives while allowing real‑world formats.

Why return the original string?

Normalization is used only for validation.

Returning the original avoids surprising the caller and preserves fidelity.

Why no control‑character regex?

To remain ESLint‑clean (no-control-regex) and avoid brittle patterns.

Whitespace and structural rules provide sufficient safety.

Example Usage

validatePhone('+15551234567');
// → "+15551234567"

validatePhone('(555) 123-4567', false);
// → "(555) 123-4567"

validatePhone('555-123-4567 x123', false);
// → "555-123-4567 x123"

validatePhone('555-123-ABCD', false);
// → undefined

validatePhone('15551234567');
// → undefined (strict mode)