Skip to content

PhoneNumber

PhoneNumber is the result object returned by parsePhoneNumber and InputController.getPhoneNumber. It is a handle on where the walk ended: digits drive one deterministic automaton compiled from Google’s metadata, and every method reads that end state.

import type { PhoneNumber, PossibilityResult } from '@telixon/core';

PhoneNumber is an interface. You never construct one; parsePhoneNumber and InputController.getPhoneNumber return instances.

export interface PhoneNumber {
isValid(): boolean;
isValidForRegion(region: RegionCode): boolean;
isPossible(): boolean;
isPossibleWithReason(): PossibilityResult;
getValidationError(): ValidationError | null;
getNumberType(): NumberType;
getNationalNumber(): string;
getCallingCode(): string | null;
getRegion(): RegionCode | null;
formatE164(): string | null;
formatNational(): string | null;
formatInternational(): string | null;
formatRfc3966(): string | null;
}
Method Returns Reads from the end state
isValid boolean whether the walk ended in an accepting state
isValidForRegion boolean whether the number matches a specific region’s patterns
isPossible boolean whether the length is one the numbering plan can accept
isPossibleWithReason PossibilityResult the length verdict as a named reason
getValidationError ValidationError | null the named way of not being in an accepting state
getNumberType NumberType the line type the accepting state carries
getNationalNumber string the national digits, national prefix removed
getCallingCode string | null the resolved calling code digits
getRegion RegionCode | null the region the number resolved to
formatE164 string | null canonical E.164
formatNational string | null national grouping, with national prefix when the format carries one
formatInternational string | null international grouping with + and calling code
formatRfc3966 string | null RFC 3966 tel: URI

All examples below share this setup:

import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';
await ensureEngineReady();
const number = parsePhoneNumber('+1 415 555 0132');

Returns true when the walk ended in an accepting state. Validity is an accepting state, and every ValidationError variant is a named way of not being in one.

number.isValid(); // true
parsePhoneNumber('415 555', { defaultRegion: 'US' }).isValid(); // false

Returns true when the number matches the given region’s patterns, independent of the region it resolved to. Matches Google libphonenumber isValidNumberForRegion. An unrecognized region code returns false.

number.isValidForRegion('US'); // true
number.isValidForRegion('GB'); // false

Returns true when the national number has a length the calling code’s numbering plan can accept, before any pattern check. 'IS_POSSIBLE_LOCAL_ONLY' counts as possible.

number.isPossible(); // true
parsePhoneNumber('+1 123 456 7890').isPossible(); // true, length fits, pattern does not
parsePhoneNumber('415 555', { defaultRegion: 'US' }).isPossible(); // false

Returns the length verdict as a named reason:

export type PossibilityResult =
| 'IS_POSSIBLE'
| 'IS_POSSIBLE_LOCAL_ONLY'
| 'INVALID_CALLING_CODE'
| 'TOO_SHORT'
| 'TOO_LONG'
| 'INVALID_LENGTH';
number.isPossibleWithReason(); // 'IS_POSSIBLE'
parsePhoneNumber('555 0132', { defaultRegion: 'US' }).isPossibleWithReason(); // 'IS_POSSIBLE_LOCAL_ONLY'
parsePhoneNumber('415 555', { defaultRegion: 'US' }).isPossibleWithReason(); // 'TOO_SHORT'
parsePhoneNumber('(415) 555-01320', { defaultRegion: 'US' }).isPossibleWithReason(); // 'TOO_LONG'
parsePhoneNumber('+41 44 668 18 000').isPossibleWithReason(); // 'INVALID_LENGTH'

Returns the structured reason the number is not in an accepting state, or null when it is valid. Every variant carries the fields needed to act on it, such as minLength on TOO_SHORT. The eight variants and when each fires are documented on ValidationError.

number.getValidationError(); // null
parsePhoneNumber('415 555', { defaultRegion: 'US' }).getValidationError();
// { kind: 'TOO_SHORT', minLength: 10 }

Returns the line type carried by the accepting state, or 'UNKNOWN' when the number is not valid. Matches Google libphonenumber getNumberType.

export declare type NumberType =
| 'FIXED_LINE'
| 'MOBILE'
| 'FIXED_LINE_OR_MOBILE'
| 'TOLL_FREE'
| 'PREMIUM_RATE'
| 'SHARED_COST'
| 'VOIP'
| 'PERSONAL_NUMBER'
| 'PAGER'
| 'UAN'
| 'VOICEMAIL'
| 'UNKNOWN';
number.getNumberType(); // 'FIXED_LINE_OR_MOBILE'
parsePhoneNumber('+44 7400 123456').getNumberType(); // 'MOBILE'
parsePhoneNumber('415 555', { defaultRegion: 'US' }).getNumberType(); // 'UNKNOWN'

Returns the national number as bare digits: everything after the calling code, with any national prefix removed. Returns an empty string, never null, when no digits resolved.

number.getNationalNumber(); // '4155550132'
parsePhoneNumber('07400 123456', { defaultRegion: 'GB' }).getNationalNumber(); // '7400123456'

Returns the resolved calling code digits, or null when none has resolved.

number.getCallingCode(); // '1'
parsePhoneNumber('').getCallingCode(); // null

Returns the region the number resolved to, or null when the digits do not determine one. Matches Google libphonenumber getRegionCodeForNumber. The region comes from the number itself; defaultRegion only tells the walk which calling code to assume.

number.getRegion(); // 'US'
parsePhoneNumber('415 555', { defaultRegion: 'US' }).getRegion(); // null

Returns canonical E.164: +, calling code, national digits. Returns null until the number is possible.

number.formatE164(); // '+14155550132'

Returns the national format with the region’s grouping. The national prefix appears when the selected format carries a prefix rule. Returns null until the number is possible.

number.formatNational(); // '(415) 555-0132'
parsePhoneNumber('07400 123456', { defaultRegion: 'GB' }).formatNational(); // '07400 123456'

Returns the international format: +, calling code, and the nationally grouped digits. Returns null until the number is possible.

number.formatInternational(); // '+1 415-555-0132'

Returns the RFC 3966 tel: URI, suitable for href. Returns null until the number is possible.

number.formatRfc3966(); // 'tel:+1-415-555-0132'

No PhoneNumber method throws. An instance can only exist after the engine is initialized, because parsePhoneNumber throws EngineNotReadyError before ensureEngineReady resolves.

+1 415 555 0132 sits in the 555-0100 through 555-0199 range that the North American Numbering Plan reserves for fictional use, yet isValid() returns true. Validity is an accepting state: it asserts that the numbering plan accepts this pattern, not that a carrier has assigned the number to a subscriber. Assignment is live carrier data that no numbering-plan metadata contains, so no metadata-driven check can answer it.

Formatting is gated on possibility, not validity

Section titled “Formatting is gated on possibility, not validity”

All four format* methods return null until the walk has reached a possible number: when no calling code can be resolved, or when the length falls outside the plan’s possible lengths. A possible-but-invalid number does format, matching Google libphonenumber.

parsePhoneNumber('').formatE164(); // null, no calling code resolved
parsePhoneNumber('415 555', { defaultRegion: 'US' }).formatE164(); // null, TOO_SHORT
parsePhoneNumber('+1 123 456 7890').formatE164(); // '+11234567890', possible but not valid

isPossibleWithReason() reports 'INVALID_CALLING_CODE' for empty input, because PossibilityResult mirrors Google libphonenumber’s possibility axis, which has no empty variant. getValidationError() distinguishes it as { kind: 'EMPTY' }.

A PhoneNumber is an immutable snapshot of one end state. Each method computes its answer on first call and caches it, so repeated queries return the cached value. See performance for what each query costs.

When the number was parsed with strict: true, isValid and getNumberType restrict to the configured region while the other methods are unaffected. See strict mode.

Query cluster:

import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';
await ensureEngineReady();
const number = parsePhoneNumber('+1 415 555 0132');
number.isValid(); // true
number.isPossibleWithReason(); // 'IS_POSSIBLE'
number.getValidationError(); // null
number.getNumberType(); // 'FIXED_LINE_OR_MOBILE'
number.getRegion(); // 'US'
number.getCallingCode(); // '1'
number.getNationalNumber(); // '4155550132'

Formats cluster:

import { ensureEngineReady, parsePhoneNumber } from '@telixon/core';
await ensureEngineReady();
const number = parsePhoneNumber('+1 415 555 0132');
number.formatE164(); // '+14155550132'
number.formatNational(); // '(415) 555-0132'
number.formatInternational(); // '+1 415-555-0132'
number.formatRfc3966(); // 'tel:+1-415-555-0132'
// Guard the null case before storing.
const partial = parsePhoneNumber('415 555', { defaultRegion: 'US' });
partial.formatE164(); // null