Typescript Tricks: Branded Types
Typescript
When working with TypeScript, you might encounter situations where two variables have the same shape but represent completely different concepts. For instance, a user ID and a product ID might both be strings, but mixing them up could lead to bugs. It would be nice if TypeScript could help you catch these errors at compile time. This is where branded types come in.
What are Branded Types?
Branded types are a TypeScript pattern that allows you to create nominally typed variables from structurally typed ones. In simpler terms, they help you differentiate between types that have the same structure but different meanings.
type UserId = string & { readonly _brand: unique symbol };
type ProductId = string & { readonly _brand: unique symbol };
// Create branded types
function createUserId(id: string): UserId {
return id as UserId;
}
function createProductId(id: string): ProductId {
return id as ProductId;
}
// Usage
const userId = createUserId("user-123");
const productId = createProductId("prod-456");
// This will error! Type 'ProductId' is not assignable to type 'UserId'
const anotherUserId: UserId = productId;
Branded types provide a couple of benefits:
- Type Safety: Prevent accidental assignments between incompatible types.
- Self-documenting Code: Makes your code more readable by clearly distinguishing between different concepts.
- Compile-time Checking: Catches potential bugs before runtime.