Type Branding in TypeScript
Type Branding in TypeScript
Section titled “Type Branding in TypeScript”What is Type Branding?
Section titled “What is Type Branding?”TypeScript uses structural typing — two types are compatible if they have the same shape. Branding adds a unique marker to make types nominally distinct.
Basic Brand Pattern
Section titled “Basic Brand Pattern”type Brand<T, B> = T & { __brand: B };
type UserId = Brand<string, "UserId">;type PostId = Brand<string, "PostId">;type Email = Brand<string, "Email">;
function getUser(id: UserId): User { /* ... */ }function getPost(id: PostId): Post { /* ... */ }
getUser("abc" as UserId); // OKgetUser("xyz" as PostId); // ❌ Error!Opaque Type (Safer Brand)
Section titled “Opaque Type (Safer Brand)”declare const OpaqueBrand: unique symbol;
type Opaque<T, B> = T & { readonly [OpaqueBrand]: B };
type Email = Opaque<string, "Email">;
function createEmail(value: string): Email { if (!value.includes("@")) throw new Error("Invalid email"); return value as Email;}
function sendEmail(to: Email, body: string): void { console.log(`Sending to ${to}`);}
const email = createEmail("alice@test.com");sendEmail(email, "Hello!"); // OKsendEmail("not-valid", "Hi"); // ❌ Error!Real Project Example
Section titled “Real Project Example”// Domain-driven design with branded typestype CustomerId = Brand<string, "CustomerId">;type OrderId = Brand<string, "OrderId">;type ProductSku = Brand<string, "SKU">;type Money = Brand<number, "USD">;
interface Customer { id: CustomerId; name: string;}
interface Order { id: OrderId; customerId: CustomerId; total: Money;}Interview Questions
Section titled “Interview Questions”Easy: Why can’t TypeScript distinguish between string types for IDs?
Medium: How does the brand pattern simulate nominal typing?
Hard: What’s the difference between a simple brand and an opaque type brand?