Skip to content
Back to blog

TypeScript Branded Types in Practice

#typescript#patterns

TypeScript's structural type system means UserId and OrderId are interchangeable if they're both string. That's a bug waiting to happen.

The Problem

function getUser(id: string) { /* ... */ }
function getOrder(id: string) { /* ... */ }

const orderId = "ord_123";
getUser(orderId); // No error — but this is wrong

Branded Types

Add a phantom brand to make types nominally distinct:

type Brand<T, B> = T & { __brand: B };

type UserId = Brand<string, "UserId">;
type OrderId = Brand<string, "OrderId">;

function getUser(id: UserId) { /* ... */ }
function getOrder(id: OrderId) { /* ... */ }

const orderId = "ord_123" as OrderId;
getUser(orderId); // TS Error!

The __brand property never exists at runtime — it's purely a compile-time guard. Zero cost, maximum safety.