ข้ามไปเนื้อหาหลัก

Branded Types — TypeScript nominal typing ที่ไม่มีใน spec

string กับ string ที่เป็น UserId หรือ Email — TypeScript มองเหมือนกัน Branded Type ทำให้ต่าง เพิ่มความปลอดภัยโดยไม่มี runtime cost

TypeScript ใช้ structural typing — UserId และ ProductId ที่เป็น string สลับกันได้โดยไม่มี error:

type UserId = string;
type ProductId = string;

function getUser(id: UserId) { ... }
const productId: ProductId = 'prod-123';
getUser(productId); // ✅ TypeScript ยอม แต่นี่คือ bug!

Branded Type เพิ่ม phantom property เพื่อให้ type ต่างกัน:

type UserId    = string & { readonly _brand: 'UserId' };
type ProductId = string & { readonly _brand: 'ProductId' };

function createUserId(id: string): UserId {
  return id as UserId; // cast ทำครั้งเดียวที่ boundary
}

const userId    = createUserId('user-456');
const productId = 'prod-123' as ProductId;

getUser(userId);    // ✅
getUser(productId); // ❌ Error: _brand ไม่ตรง

ไม่มี runtime cost — _brand ไม่มีอยู่จริงในค่า เป็นแค่ type-level annotation ที่ TypeScript ใช้ตรวจสอบ