Skip to content
Branded Types
step 1/5

Reading — step 1 of 5

Learn

~1 min readBranded Types and Guards

TypeScript's structural typing means string is string everywhere. Branded types let you create distinct types that share a runtime representation.

The problem:

function sendEmail(userId: string, email: string) { ... }

sendEmail('[email protected]', 'user-123');    // SWAPPED — TypeScript can't catch this

The fix with brands:

type UserId = string & { readonly __brand: unique symbol };
type Email = string & { readonly __brand: unique symbol };

function asUserId(s: string): UserId {
    // (validate format, throw on bad input)
    return s as UserId;
}

function asEmail(s: string): Email {
    if (!s.includes('@')) throw new Error('not an email');
    return s as Email;
}

function sendEmail(userId: UserId, email: Email) { ... }

sendEmail(asUserId('user-123'), asEmail('[email protected]'));     // OK
sendEmail('[email protected]', 'user-123');                         // ERROR — not branded

The unique symbol brand has zero runtime existence — pure type-level distinction.

Common use cases:

  • Database IDs (UserId vs PostId vs OrderId)
  • Validated strings (Email, URL, NonEmptyString, ISODateString)
  • Numeric units (Pixels vs Inches vs Seconds)
  • Currencies (USD vs EUR — never accidentally mix)
type Brand<T, B> = T & { readonly __brand: B };

type USD = Brand<number, 'USD'>;
type EUR = Brand<number, 'EUR'>;

function toUSD(n: number): USD { return n as USD; }
function toEUR(n: number): EUR { return n as EUR; }

function addUSD(a: USD, b: USD): USD { return (a + b) as USD; }

const price = toUSD(10);
const tax = toUSD(2);
addUSD(price, tax);                  // OK
// addUSD(price, toEUR(2));         // ERROR

TypeScript 4.7+ has unique symbol for stricter brands. TypeScript 5.0+ added accessor syntax for cleaner brand patterns.

This pattern is used heavily in big TypeScript codebases (Stripe, Sentry, Microsoft) to prevent ID-confusion bugs.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…