@arctics/phonify
v1.2.0
Published
A utility library for phone number parsing and validation.
Maintainers
Readme
@arctics/phonify
Table of Contents
- Introduction
- Features
- Installation
- Quick Start
- Usage
- Examples
- API Reference
- Advanced Features
- Contributing
- License
Introduction
@arctics/phonify is a comprehensive, lightweight TypeScript library for parsing, validating, and formatting international phone numbers. Built with extensive phone number metadata from 200+ territories, it provides accurate validation, type detection (mobile, fixed-line, toll-free, etc.), and intelligent formatting support for phone numbers in both national and international formats.
Whether you're building a communication platform, processing bulk phone data, or implementing real-time phone number formatting, Phonify gives you the tools to handle phone numbers with confidence.
Features
✨ Comprehensive Coverage
- Support for 200+ territories and country codes
- Accurate validation based on regional phone number patterns
- Automatic territory detection from international numbers
🎯 Type Detection
- Identify phone number types: mobile, fixed-line, toll-free, premium rate, VoIP, and more
- Region-specific pattern matching
- Unknown type fallback for edge cases
📞 Smart Formatting
- National and international (E.164) format support
- Real-time "as-you-type" formatting with intelligent spacing
- Automatic country code detection for international numbers
- Tel URI generation for clickable phone links
🔍 Validation
- Length validation (TOO_SHORT, TOO_LONG, VALID)
- Pattern-based validation against country-specific rules
- National prefix handling for complex cases (e.g., Argentina, Germany)
- Support for incomplete numbers during typing
⚡ Developer-Friendly
- Full TypeScript support with comprehensive types
- Simple, intuitive API with zero configuration
- No external dependencies
- 88%+ code coverage with 68+ test cases
Installation
npm install @arctics/phonifyor
yarn add @arctics/phonifyQuick Start
import {
parsePhoneNumber,
isValidPhoneNumber,
AsYouType,
} from '@arctics/phonify';
// Parse a phone number
const phone = parsePhoneNumber('+442070313000');
console.log(phone?.country); // 'GB'
console.log(phone?.number); // '+442070313000' (E.164)
console.log(phone?.getType()); // 'fixedLine'
console.log(phone?.isValid()); // true
// Quick validation
isValidPhoneNumber('2070313000', 'GB'); // true
// Format as-you-type
const formatter = new AsYouType('US');
formatter.input('2'); // '2'
formatter.input('0'); // '20'
formatter.input('1'); // '201'
formatter.input('5'); // '201 5'
formatter.input('5'); // '201 55'
formatter.input('5'); // '201 555'
formatter.input('0'); // '201 555 0'Usage
Basic Parsing
import { PhoneNumber, parsePhoneNumber } from '@arctics/phonify';
// International format
const phone1 = PhoneNumber.parse('+12015550123');
// Result: PhoneNumber instance for USA with national number '2015550123'
// National format with country hint
const phone2 = PhoneNumber.parse('2070313000', 'GB');
// Result: PhoneNumber instance for UK
// With national prefix
const phone3 = PhoneNumber.parse('02070313000', 'GB');
// Automatically strips the '0' prefix, result valid for UK
// Legacy function style (still supported)
import { parsePhoneNumber } from '@arctics/phonify';
const phone4 = parsePhoneNumber('+12015550123');
Validation
import {
isValidPhoneNumber,
validatePhoneNumberLength,
} from '@arctics/phonify';
// Simple boolean validation
const isValid = isValidPhoneNumber('2015550123', 'US');
// Returns: true if matches country pattern, false otherwise
// Detailed length validation
const lengthResult = validatePhoneNumberLength('201', 'US');
// Returns: 'TOO_SHORT' if below minimum
// Returns: 'TOO_LONG' if exceeds maximum
// Returns: 'INVALID_COUNTRY' if country not found
// Returns: undefined if validType Detection
import { parsePhoneNumber } from '@arctics/phonify';
const phone = parsePhoneNumber('+442070313000', 'GB');
const type = phone?.getType();
// Possible values:
// - 'fixedLine'
// - 'mobile'
// - 'tollFree'
// - 'premiumRate'
// - 'sharedCost'
// - 'voip'
// - 'personalNumber'
// - 'uan'
// - 'pager'
// - 'voicemail'
// - 'shortCode'
// - 'emergency'
// - 'UNKNOWN'Formatting
import { parsePhoneNumber, PhoneNumberFormat } from '@arctics/phonify';
const phone = parsePhoneNumber('+442070313000');
// Unified Format API
console.log(phone?.format(PhoneNumberFormat.E164)); // '+442070313000'
console.log(phone?.format(PhoneNumberFormat.INTERNATIONAL)); // '+44 207 0313 000'
console.log(phone?.format(PhoneNumberFormat.NATIONAL)); // '207 0313 000'
console.log(phone?.format(PhoneNumberFormat.RFC3966)); // 'tel:+442070313000'
// Legacy methods (still supported)
console.log(phone?.number); // '+442070313000'
console.log(phone?.formatNational()); // '207 0313 000'
console.log(phone?.formatInternational());// '+44 207 0313 000'
console.log(phone?.getURI()); // 'tel:+442070313000'
Extension Support
The library now supports parsing and formatting of phone number extensions.
Parsing
Extensions are automatically extracted from input strings using common delimiters (ext., x, #, ;ext=).
import { parsePhoneNumber } from '@arctics/phonify';
const phone = parsePhoneNumber('+1 (201) 555-0123 ext. 456');
console.log(phone?.extension); // '456'Formatting
Extensions are included in formatted output where appropriate.
// International Format
console.log(phone?.formatInternational()); // '+1 201-555-0123 ext. 456'
// RFC3966 (URI)
console.log(phone?.getURI()); // 'tel:+12015550123;ext=456'Real-Time Formatting with AsYouType
import { AsYouType } from '@arctics/phonify';
const formatter = new AsYouType('GB');
// Simulate user typing
formatter.input('2'); // '2'
formatter.input('0'); // '20'
formatter.input('7'); // '207'
formatter.input('0'); // '207 0'
formatter.input('3'); // '207 03'
formatter.input('1'); // '207 031'
formatter.input('3'); // '207 0313'
// Automatic country detection for international
formatter.reset();
formatter.input('+'); // '+'
formatter.input('4'); // '+4'
formatter.input('4'); // '+44 ' (detects UK)
formatter.input('2'); // '+44 2'
formatter.input('0'); // '+44 20'Examples
Example 1: Contact Form Validation
import { parsePhoneNumber, isValidPhoneNumber } from '@arctics/phonify';
function validateContactForm(
phone: string,
country: string,
): {
valid: boolean;
error?: string;
formatted?: string;
} {
if (!phone || !country) {
return { valid: false, error: 'Phone and country are required' };
}
if (!isValidPhoneNumber(phone, country)) {
return { valid: false, error: 'Invalid phone number' };
}
const parsed = parsePhoneNumber(phone, country);
return {
valid: true,
formatted: parsed?.number,
};
}
// Usage
const result = validateContactForm('2070313000', 'GB');
console.log(result);
// { valid: true, formatted: '+442070313000' }Example 2: Detecting Number Type for Routing
import { parsePhoneNumber } from '@arctics/phonify';
function routeCall(phoneNumber: string) {
const parsed = parsePhoneNumber(phoneNumber);
if (!parsed?.isValid()) {
return { route: 'ERROR', reason: 'Invalid number' };
}
const type = parsed.getType();
switch (type) {
case 'mobile':
return { route: 'MOBILE_NETWORK', number: parsed.number };
case 'fixedLine':
return { route: 'PSTN', number: parsed.number };
case 'tollFree':
return { route: 'TOLL_FREE_SERVICE', number: parsed.number };
case 'voip':
return { route: 'VOIP_PROVIDER', number: parsed.number };
default:
return { route: 'FALLBACK', number: parsed.number };
}
}
// Usage
console.log(routeCall('+442070313000'));
// { route: 'PSTN', number: '+442070313000' }Example 3: Input Masking with AsYouType
import { AsYouType } from '@arctics/phonify';
function createPhoneInput(countryCode: string) {
const formatter = new AsYouType(countryCode);
return {
onInput: (event: any) => {
const input = event.target.value;
const formatted = formatter.input(input.slice(-1));
// Update input display
event.target.value = formatted;
return formatted;
},
reset: () => {
formatter.reset();
},
};
}
// Usage in React/Vue/etc
const usInput = createPhoneInput('US');
// User types: 2 -> 0 -> 1 -> 5 -> 5 -> 5 -> 0 -> 1 -> 2 -> 3
// Display updates: 2 -> 20 -> 201 -> 201 5 -> 201 55 -> 201 555 -> 201 555 0 -> ...API Reference
Functions
parsePhoneNumber(input: string, country?: string): PhoneNumber | null
Parses a phone number string and returns a PhoneNumber instance or null if invalid.
Parameters:
input- Phone number string (can be international format with +, or national format)country- Optional ISO 3166-1 alpha-2 country code for national format numbers
Returns: PhoneNumber instance or null if parsing fails
Examples:
parsePhoneNumber('+442070313000'); // International
parsePhoneNumber('2070313000', 'GB'); // National with country
parsePhoneNumber('02070313000', 'GB'); // National with prefixisValidPhoneNumber(input: string, country: string): boolean
Quick validation check for a phone number.
Parameters:
input- Phone number stringcountry- ISO 3166-1 alpha-2 country code
Returns: true if valid, false otherwise
Examples:
isValidPhoneNumber('2070313000', 'GB'); // true
isValidPhoneNumber('invalid', 'GB'); // falsevalidatePhoneNumberLength(input: string, country: string): ValidationResult
Validates phone number length and returns detailed result.
Parameters:
input- Phone number stringcountry- ISO 3166-1 alpha-2 country code
Returns: 'TOO_SHORT' | 'TOO_LONG' | 'INVALID_COUNTRY' | undefined
Examples:
validatePhoneNumberLength('201', 'GB'); // 'TOO_SHORT'
validatePhoneNumberLength('2070313000', 'GB'); // undefined (valid)Classes
PhoneNumber
Represents a parsed and validated phone number.
Properties:
nationalNumber: string- Phone number without country codecountry: string- ISO 3166-1 alpha-2 country codenumber: string- E.164 format (+CC + national number)
Methods:
isValid(): boolean
Validates the number against country-specific patterns.
getType(): PhoneType | 'UNKNOWN'
Determines the phone number type (mobile, fixedLine, etc.).
formatNational(): string
Formats the number in national format with optional prefix and spacing.
formatInternational(): string
Formats the number in international format with country code.
getURI(): string
Returns a tel: URI for clickable phone links.
AsYouType
Provides real-time formatting as users type phone numbers.
Constructor:
new AsYouType(countryCode: string)Parameters:
countryCode- Default ISO 3166-1 alpha-2 country code for national format numbers
Static Methods
PhoneNumber.parse(number: string, region?: string): PhoneNumber | undefined
Parses a number string into a PhoneNumber object.
isPossibleNumber(): boolean
Lightweight check if the number length is possible for the region (faster than full regex validation).
isMobile(): boolean, isFixedLine(): boolean, etc.
Helper predicates for checking number type.
Methods:
input(text: string): string
Adds a character to the input and returns formatted result.
Parameters:
text- Single character or string to add
Returns: Formatted phone number string
reset(): void
Clears the current input state.
Examples:
const formatter = new AsYouType('US');
formatter.input('2'); // '2'
formatter.input('0'); // '20'
formatter.input('1'); // '201'
formatter.reset(); // State cleared
formatter.input('1'); // '1' (fresh start)Advanced Features
National Prefix Handling
The library automatically handles national prefixes for different countries:
// UK: National prefix '0' is automatically removed
parsePhoneNumber('02070313000', 'GB');
// Equivalent to: parsePhoneNumber('2070313000', 'GB')
// Germany: Complex prefix rules handled automatically
parsePhoneNumber('0301234567', 'DE');
// National prefix removed during parsingCountry Code Detection
International numbers are automatically detected and routed to the correct country:
// Single digit country codes
parsePhoneNumber('+12015550123'); // USA (country code: 1)
// Two digit country codes
parsePhoneNumber('+442070313000'); // UK (country code: 44)
// Three digit country codes
parsePhoneNumber('+882123456789'); // Iridium (country code: 882)Incomplete Number Support
The library supports incomplete numbers during real-time input:
const formatter = new AsYouType('US');
formatter.input('2'); // '2' - incomplete but accepted
formatter.input('0'); // '20' - still incomplete
// Later validation can check if number is completeContributing
Contributions are welcome! Please follow these steps:
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
License
This project is licensed under the MIT License.
