Codectionary / Developer documentation / TypeScript

Branded Types for Validated Domain Values

A branded type intersects a primitive with a distinctive marker to discourage mixing values that share the same runtime representation. This is a TypeScript design pattern built on structural typing and unique symbols, not a runtime wrapper. A validating factory should be the controlled point where a brand is asserted.

Syntax

declare const idBrand: unique symbol;
type UserId = string & { readonly [idBrand]: true };

Examples

Branded Types for Validated Domain Values: working example

Run this standalone example after compiling with TypeScript 5.4+ in strict mode, targeting ES2022. Expected result: The program prints Lookup user_42; plain strings require validation before use.

declare const userIdBrand: unique symbol;
type UserId = string & { readonly [userIdBrand]: true };
function userId(value: string): UserId {
  if (!/^user_[0-9]+$/.test(value)) throw new Error("Invalid user ID");
  return value as UserId;
}
function describe(id: UserId): string { return `Lookup ${id}`; }
console.log(describe(userId("user_42")));
// describe("user_42"); // Type error: plain strings are not branded.
  1. Expected result: The program prints Lookup user_42; plain strings require validation before use.
  2. The unique symbol marker distinguishes this domain type during checking.
  3. No symbol property is attached at runtime; the factory verifies the chosen invariant before asserting the brand.

Best practices

  • Keep brand assertions inside small audited factories.
  • Revalidate untrusted values after deserialization because types are erased.

Runtime & versions

Compiled to JavaScript. Feature availability depends on the language and runtime version used by your project; consult the official reference below.

TypeScript Handbook

Specifications & further reading