Branded types help keep different kinds of data separate even if they look the same. This stops mistakes where you mix things up by accident.
Branded types for safety in Typescript
type Brand<K, T> = K & { __brand: T };
// Example:
type UserID = Brand<string, 'UserID'>;
type ProductID = Brand<string, 'ProductID'>;The Brand type combines a base type K with a unique tag T to create a new type.
This tag __brand does not exist at runtime; it only helps TypeScript check types.
type UserID = Brand<string, 'UserID'>; type ProductID = Brand<string, 'ProductID'>; const userId: UserID = 'abc123' as UserID; const productId: ProductID = 'abc123' as ProductID; // Error if you try to assign one to the other: // const wrong: UserID = productId;
function getUser(id: UserID) { console.log(`User ID is ${id}`); } const id = 'user-1' as UserID; getUser(id);
This program defines two branded types and a function that only accepts UserID. It shows how TypeScript prevents mixing them up.
type Brand<K, T> = K & { __brand: T };
type UserID = Brand<string, 'UserID'>;
type ProductID = Brand<string, 'ProductID'>;
function printUserId(id: UserID) {
console.log(`User ID: ${id}`);
}
const userId = 'user123' as UserID;
const productId = 'prod123' as ProductID;
printUserId(userId);
// The next line would cause a TypeScript error if uncommented:
// printUserId(productId);Branded types only exist during development and do not add code at runtime.
You must use as to tell TypeScript when you want to treat a value as a branded type.
This technique helps catch bugs early by making types more specific.
Branded types add a hidden tag to simple types to keep them distinct.
They help avoid mixing up similar data like IDs or codes.
They improve safety without changing runtime behavior.
Practice
branded types in TypeScript?Solution
Step 1: Understand branded types concept
Branded types add a unique tag to simple types like string or number to keep them distinct in the type system.Step 2: Identify the purpose of branding
This helps avoid mixing similar data types (like user IDs and product IDs) by making them incompatible even if they share the same base type.Final Answer:
To create distinct types from simple types for better type safety -> Option BQuick Check:
Branded types = distinct types for safety [OK]
- Thinking branded types improve runtime speed
- Confusing branded types with adding methods
- Assuming branded types convert types automatically
string called UserId?Solution
Step 1: Recall branded type syntax
Branded types use intersection (&) to combine a base type with a unique tag object.Step 2: Check each option
type UserId = string & { __brand: 'UserId' } correctly usesstring & { __brand: 'UserId' }. type UserId = string | { __brand: 'UserId' } uses union (|) which allows either type, not branding. type UserId = { __brand: 'UserId' } misses base type. type UserId = string extends { __brand: 'UserId' } is invalid syntax.Final Answer:
type UserId = string & { __brand: 'UserId' } -> Option CQuick Check:
Branded type = base & unique tag [OK]
- Using union (|) instead of intersection (&)
- Omitting the base type in branded type
- Writing invalid TypeScript syntax
type UserId = string & { __brand: 'UserId' };
const id1 = 'abc123' as UserId;
const id2 = 'xyz789' as UserId;
function isSameUser(a: UserId, b: UserId) {
return a === b;
}
console.log(isSameUser(id1, id2));Solution
Step 1: Understand branded types at runtime
Branded types only exist at compile time for safety. At runtime, they are just strings.Step 2: Evaluate the comparison
Since id1 is 'abc123' and id2 is 'xyz789', the comparisona === breturns false.Final Answer:
false -> Option AQuick Check:
Branded types = strings at runtime, so 'abc123' === 'xyz789' is false [OK]
- Expecting branded types to cause runtime errors
- Thinking branded types add runtime checks
- Assuming same brand means always equal
type ProductId = string & { __brand: 'ProductId' };
function getProduct(id: ProductId) {
return `Product: ${id}`;
}
const id = '123' as string;
getProduct(id);Solution
Step 1: Check function parameter type
getProduct expects aProductId, which is a branded string type.Step 2: Analyze argument passed
The argumentidis typed as plainstring, not branded. TypeScript disallows passing plain string where branded type is expected.Final Answer:
Argument of type 'string' is not assignable to parameter of type 'ProductId' -> Option AQuick Check:
Branded type requires explicit cast or branded value [OK]
- Passing plain string without branding
- Ignoring TypeScript type errors
- Assuming branding affects runtime
type UserId = string & { __brand: 'UserId' };type OrderId = string & { __brand: 'OrderId' };How can you safely create a function that accepts either
UserId or OrderId but not plain strings, and returns a string description?Solution
Step 1: Understand union of branded types
UsingUserId | OrderIdallows the function to accept either branded type but excludes plain strings.Step 2: Check other options
function describeId(id: string) { return `ID: ${id}`; } accepts plain strings, which is unsafe. function describeId(id: UserId & OrderId) { return `ID: ${id}`; } requires a value that is both brands simultaneously, which is impossible. function describeId(id: unknown) { return `ID: ${id}`; } accepts any type, losing type safety.Final Answer:
function describeId(id: UserId | OrderId) { return `ID: ${id}`; } -> Option DQuick Check:
Use union of branded types to accept multiple safe types [OK]
- Using intersection (&) instead of union (|)
- Accepting plain strings losing safety
- Using unknown type losing type checks
Ready to prove it?
Pass the test and this pattern moves unknown
