What are branded types (opaque types) in TypeScript?

Answer

Since TypeScript uses structural typing, two types with the same structure are interchangeable — which is sometimes undesirable. Branded types (opaque types) use a phantom property trick to make structurally identical types incompatible, simulating nominal typing. Pattern: type UserId = string & { readonly _brand: "UserId" }; type PostId = string & { readonly _brand: "PostId" };. Both are strings at runtime, but TypeScript treats them as different types — you cannot accidentally pass a PostId where a UserId is expected. Create branded values with a cast: function toUserId(id: string): UserId { return id as UserId; }. Common use cases: ID types (user ID, order ID), validated values (SanitizedString, EmailAddress, ValidatedEmail), units (Meters, Kilograms). The phantom property (_brand) never exists at runtime — it is purely a type-system trick. More ergonomic with a helper: type Brand<T, B> = T & { readonly _brand: B }; type UserId = Brand<string, "UserId">;.