Branded types (also called nominal types) allow you to distinguish values that have the same runtime type but different semantic meaning:
type UserId = string & { readonly _brand: 'UserId' };
type ChallengeId = string & { readonly _brand: 'ChallengeId' };
function getChallenge(id: ChallengeId): Challenge { ... }
const userId: UserId = makeUserId('u_123');
getChallenge(userId); // ❌ TypeScript error — UserId ≠ ChallengeIdThe _brand field is a phantom type — it only exists at compile time. At runtime, the value is just a string.
Instead of isSlug(x): boolean, return the branded type or throw:
// ❌ validate (you lose the type information):
if (isSlug(value)) { use(value); } // value is still string
// ✅ parse (the return type carries the proof):
const slug = parseSlug(value); // Slug | throws
use(slug); // slug is Slug ✅type Slug = string & { readonly _brand: 'Slug' };
type HexColor = string & { readonly _brand: 'HexColor' };
// Slug: lowercase letters, digits, hyphens — at least 2 chars
// Valid: 'hello-world', 'ts-generics-easy', 'my-challenge-1'
// Invalid: 'Hello', '-start', 'end-', 'a' (too short)
// HexColor: #RGB or #RRGGBB
// Valid: '#FFF', '#ff0000', '#A3B'
// Invalid: 'red', '#GGG', '#1234567' (7 digits)Sample tests