typescript 50 lines · 7 steps

Normalizing phone numbers to E.164 in TypeScript

Parse arbitrary phone input into a validated, canonical shape, with both throwing and safe variants.

Explained by highlit
1import { parsePhoneNumberFromString, CountryCode } from 'libphonenumber-js';
2 
3export interface NormalizedPhone {
4 e164: string;
5 country: CountryCode;
6 nationalNumber: string;
7 type: string | undefined;
8}
9 
10export class InvalidPhoneNumberError extends Error {
11 constructor(input: string) {
12 super(`Invalid phone number: ${input}`);
13 this.name = 'InvalidPhoneNumberError';
14 }
15}
16 
17export function normalizePhoneNumber(
18 input: string,
19 defaultCountry: CountryCode = 'US',
20): NormalizedPhone {
21 const trimmed = input.trim();
22 
23 if (!trimmed) {
24 throw new InvalidPhoneNumberError(input);
25 }
26 
27 const parsed = parsePhoneNumberFromString(trimmed, defaultCountry);
28 
29 if (!parsed || !parsed.isValid()) {
30 throw new InvalidPhoneNumberError(input);
31 }
32 
33 return {
34 e164: parsed.number,
35 country: parsed.country ?? defaultCountry,
36 nationalNumber: parsed.nationalNumber,
37 type: parsed.getType(),
38 };
39}
40 
41export function tryNormalizePhoneNumber(
42 input: string,
43 defaultCountry: CountryCode = 'US',
44): NormalizedPhone | null {
45 try {
46 return normalizePhoneNumber(input, defaultCountry);
47 } catch {
48 return null;
49 }
50}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Normalizing to a canonical format like E.164 makes stored phone numbers comparable and unambiguous.
  2. 2A custom error subclass lets callers distinguish invalid input from other failures precisely.
  3. 3Offering both a throwing and a try/catch-wrapping variant lets callers choose the ergonomics they want.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Normalizing phone numbers to E.164 in TypeScript — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code