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.