10-branded-types.ts
Typescript/src/03-advanced/10-branded-types.ts
// ============================================
// 10. Branded Types and Opaque Types
// ============================================
// Branded Types
type Brand<T, B> = T & { __brand: B };
type UserId = Brand<string, "UserId">;
type ProductId = Brand<string, "ProductId">;
function getUser(id: UserId): { id: UserId; name: string } {
return { id, name: "Alice" };
}
function getProduct(id: ProductId): { id: ProductId; name: string } {
return { id, name: "Product" };
}
const userId: UserId = "123" as UserId;
const productId: ProductId = "456" as ProductId;
getUser(userId); // OK
// getUser(productId); // Error: Type 'ProductId' is not assignable to type 'UserId'
// Opaque Types
declare const __opaque: unique symbol;
type Opaque<T, K> = T & { readonly [__opaque]: K };
type Email = Opaque<string, "Email">;
type Password = Opaque<string, "Password">;
function createEmail(value: string): Email {
if (!value.includes("@")) {
throw new Error("Invalid email");
}
return value as Email;
}
function createPassword(value: string): Password {
if (value.length < 8) {
throw new Error("Password too short");
}
return value as Password;
}
const email: Email = createEmail("user@example.com");
const password: Password = createPassword("secure123");
// email and password are distinct types
// email = password; // Error: Type 'Password' is not assignable to type 'Email'
// Nominal Typing
type Meters = Brand<number, "Meters">;
type Feet = Brand<number, "Feet">;
function convertToFeet(meters: Meters): Feet {
return (meters * 3.28084) as Feet;
}
const distance: Meters = 10 as Meters;
const inFeet = convertToFeet(distance); // Type-safe conversion
// Type-Safe IDs
type EntityId<T> = Brand<string, T>;
type UserId2 = EntityId<"User">;
type PostId = EntityId<"Post">;
type CommentId = EntityId<"Comment">;
interface User2 {
id: UserId2;
name: string;
}
interface Post {
id: PostId;
title: string;
authorId: UserId2;
}
function getUser2(id: UserId2): User2 | undefined {
// Implementation
return undefined;
}
const userId2: UserId2 = "user-123" as UserId2;
const postId: PostId = "post-456" as PostId;
getUser2(userId2); // OK
// getUser2(postId); // Error: Type 'PostId' is not assignable to type 'UserId2'
export { getUser, createEmail, createPassword, convertToFeet, getUser2 };
関連記事
01-introduction.ts
01-introduction.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/01-introduction.ts).
記事を読む →02-basic-types.ts
02-basic-types.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/02-basic-types.ts).
記事を読む →03-variables.ts
03-variables.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/03-variables.ts).
記事を読む →04-functions.ts
04-functions.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/04-functions.ts).
記事を読む →05-interfaces.ts
05-interfaces.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/05-interfaces.ts).
記事を読む →06-classes.ts
06-classes.ts — typescript source code from the Typescript learning materials (Typescript/src/01-beginner/06-classes.ts).
記事を読む →