Advanced Patterns

Branded Types

Branded types (also called nominal types or opaque types) add a phantom property to create distinct types from structurally identical base types.

Definition

TypeScript
type Brand<T, B> = T & { __brand: B };
type USD = Brand<number, "USD">;
type EUR = Brand<number, "EUR">;

Examples

Currency Safety
type Brand<T, B extends string> = T & { readonly __brand: B };

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

function usd(amount: number): USD {
  return amount as USD;
}

function eur(amount: number): EUR {
  return amount as EUR;
}

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

const price = usd(100);
const tax = usd(8.5);
const total = addUSD(price, tax); // OK
// addUSD(price, eur(50)); // Error: EUR not assignable to USD
Entity IDs
type UserId = Brand<string, "UserId">;
type PostId = Brand<string, "PostId">;

function userId(id: string): UserId { return id as UserId; }
function postId(id: string): PostId { return id as PostId; }

function getUser(id: UserId) { /* ... */ }
function getPost(id: PostId) { /* ... */ }

const uid = userId("user-123");
const pid = postId("post-456");

getUser(uid); // OK
// getUser(pid); // Error: PostId not assignable to UserId
Validated Strings
type Email = Brand<string, "Email">;
type URL = Brand<string, "URL">;

function validateEmail(input: string): Email | null {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return regex.test(input) ? (input as Email) : null;
}

function validateURL(input: string): URL | null {
  try {
    new globalThis.URL(input);
    return input as URL;
  } catch {
    return null;
  }
}

function sendEmail(to: Email, link: URL) { /* ... */ }

const email = validateEmail("[email protected]");
const url = validateURL("https://example.com");
if (email && url) sendEmail(email, url);

Common Use Cases

  • 1Preventing accidental mixing of same-shaped IDs
  • 2Type-safe currency and unit handling
  • 3Validated input types (email, URL, phone)
  • 4Domain-driven design with distinct value objects
  • 5Preventing unit confusion in scientific computing

Understanding Branded Types

Branded types (also known as nominal types or opaque types) solve a fundamental limitation of TypeScript's structural type system. In TypeScript, two types are compatible if they have the same structure, regardless of their names. This means a UserId (string) and a PostId (string) are interchangeable, which can lead to bugs when IDs are accidentally swapped.

Branding adds a phantom property (typically __brand) to create structurally distinct types from the same base type. Brand<number, "USD"> and Brand<number, "EUR"> are both numbers at runtime, but TypeScript treats them as incompatible types, preventing you from accidentally adding dollars to euros.

The pattern requires constructor functions that cast plain values to branded types. These constructors serve as validation boundaries—the only way to create a branded value is through the constructor, which can perform validation. Once a value is branded, the type system ensures it's used correctly throughout the codebase.

Entity IDs are the most common use case. In any application with multiple entity types, accidentally passing a UserId where a PostId is expected is a real bug that structural typing can't catch. Branded IDs catch this at compile time with zero runtime overhead—the brand property only exists in the type system.

Validated strings are another powerful application. By branding a string as Email only after validation, you encode the validation status in the type. Functions that require validated input accept Email (not string), and the compiler ensures validation happened before the value reaches those functions.

Branded types have no runtime cost. The __brand property exists purely in the type system and is erased during compilation. The runtime values are plain strings, numbers, or whatever the base type is.

Related Types

More Advanced Patterns

Explore TypeScript Types

Browse our complete reference of 30 TypeScript utility types with definitions, examples, and explanations.