TypeScript
Using brands
Learn to use branded types in functions.
Branded parameters
Using branded types as function parameters prevents values of the wrong type from being passed. Instead of a plain string, only values with the correct brand are accepted; this catches confusion at compile time.
type UserId = Brand<string, "UserId">;
function getUser(id: UserId) {
return `user ${id}`;
}
const uid = "u1" as UserId;
getUser(uid); // valid
getUser("u1"); // error: not a plain string
Creating brands with validation
To create branded values safely, helper functions that validate are used. These functions check the input, brand it with as and return it if valid; throw an error if invalid. This way the brand is only created with valid values.
type Email = Brand<string, "Email">;
function makeEmail(s: string): Email {
if (!s.includes("@")) throw new Error("invalid email");
return s as Email;
}
const e = makeEmail("a@b.com"); // valid
Exercise Create a brand with a validation helper
Define the Brand<T, B> helper type with intersection (using string brand). Create a UserId type. Write a makeUserId(s: string): UserId function that checks for empty string, returns with as UserId if valid, or throw new Error("empty") otherwise. Create a value via makeUserId("u1") and print it with console.log.
Show example solution
type Brand<T, B extends string> = T & { readonly __brand: B };
type UserId = Brand<string, "UserId">;
function makeUserId(s: string): UserId {
if (s.length === 0) throw new Error("empty");
return s as UserId;
}
const uid = makeUserId("u1");
console.log(uid);
Test yourself
-
Which of the following is a safety provided by a branded function parameter?
- String values are automatically converted to the brand
- Values with the wrong brand are rejected at compile time
- The function runs faster
- Brand check performed at runtime
Show answer
Values with the wrong brand are rejected at compile time — Branded parameters provide compile-time type safety; values with the wrong brand (for example passing OrderId to a UserId parameter) cause a compile error.
-
Return a branded value after validation
type Brand<T, B extends string> = T & { readonly __brand: B };
type UserId = Brand<string, "UserId">;
function makeUserId(s: string): UserId {
if (s.length === 0) throw new Error("boş");
return s ____;
}
Show answer
type Brand<T, B extends string> = T & { readonly __brand: B };
type UserId = Brand<string, "UserId">;
function makeUserId(s: string): UserId {
if (s.length === 0) throw new Error("boş");
return s as UserId;
}
-
Brand usage parts
- Brand'li parametre
- rejects wrong brand at compile time
- as UserId
- branding after validation
- makeUserId(s)
- validation and brand creation
- getUser("u1")
- compile error (plain string)
Next lesson: Summary - Branded types