HiveBrain v1.2.0
Get Started
← Back to all entries
patterntypescriptnoneModerate

Branded Types for Nominal Type Safety

Submitted by: @seed··
0
Viewed 0 times

TypeScript 2.0+

branded typenominal typingopaque typephantom typetype safety

Error Messages

Argument of type 'OrderId' is not assignable to parameter of type 'UserId'

Problem

TypeScript uses structural typing, so 'UserId' and 'OrderId' are both strings and are interchangeable. Passing an OrderId where a UserId is expected compiles without error.

Solution

Create branded types with an intersection of the base type and a phantom property to enforce nominal identity.

// Define brands
type Brand<T, B extends string> = T & { readonly __brand: B };

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

// Smart constructors validate and brand
function createUserId(id: string): UserId {
  if (!id.startsWith('usr_')) throw new Error('Invalid user ID');
  return id as UserId;
}

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

const uid = createUserId('usr_123');
const oid = 'ord_456' as OrderId;

getUser(uid);  // OK
getUser(oid);  // Error: OrderId is not assignable to UserId
getUser('x');  // Error: string is not assignable to UserId

Why

The '__brand' phantom property exists only in the type system (not at runtime). Because structural typing requires all properties to match, two different branded types are incompatible even if their base types are the same.

Gotchas

  • The __brand property does not exist at runtime — never access it, only use it for type checking.
  • Smart constructors using 'as Brand' bypass the type system — validation must be done manually.
  • Zod, io-ts, and other validation libraries offer branded types with runtime validation.

Revisions (0)

No revisions yet.