Category: reference
TypeScript satisfies & const Assertions
satisfies operator (TS 4.9) และ as const ช่วย infer types ที่แคบลงโดยไม่ต้อง annotate — ทำให้โค้ดปลอดภัยขึ้นโดยไม่เสีย flexibility
สารบัญ
satisfies operator (TypeScript 4.9+)
ปัญหาของ type annotation ปกติ: ทำให้ type กว้างขึ้น ทำให้เสีย narrowing
// ❌ annotate แบบ type annotation — type กว้างขึ้น
const palette: Record<string, string | string[]> = {
red: '#ef4444',
green: ['#4ade80', '#16a34a'],
blue: '#3b82f6',
};
palette.red.toUpperCase(); // ❌ Error: string | string[] ไม่มี toUpperCase
// ✓ satisfies — validate กับ type แต่ยัง infer type ที่แคบกว่า
const palette = {
red: '#ef4444',
green: ['#4ade80', '#16a34a'],
blue: '#3b82f6',
} satisfies Record<string, string | string[]>;
palette.red.toUpperCase(); // ✓ TypeScript รู้ว่า red เป็น string
palette.green.includes('#4ade80'); // ✓ รู้ว่า green เป็น string[]
satisfies vs Type Annotation vs as
type Config = { port: number; host: string };
// 1. Type annotation — lose narrowing
const a: Config = { port: 3000, host: 'localhost' };
a.port; // number
// 2. satisfies — validate + keep narrow type
const b = { port: 3000, host: 'localhost' } satisfies Config;
b.port; // 3000 (literal type ไม่ถูก widen)
// 3. as Config — unsafe cast, ไม่มี validation
const c = { port: 3000, host: 'localhost' } as Config;
// ❌ ไม่ validate: { port: 3000 } as Config จะผ่านแม้ขาด host
Use Cases ของ satisfies
1. Object ที่มีค่าหลาย types
type Routes = Record<string, { path: string; auth?: boolean }>;
const routes = {
home: { path: '/' },
profile: { path: '/profile', auth: true },
login: { path: '/login' },
} satisfies Routes;
// ✓ รู้ว่า auth เป็น true ไม่ใช่ boolean | undefined สำหรับ profile
routes.profile.auth; // true
2. Enum-like objects
type Status = 'active' | 'completed' | 'archived';
type StatusConfig = Record<Status, { label: string; color: string }>;
const STATUS = {
active: { label: 'กำลังทำ', color: '#10b981' },
completed: { label: 'เสร็จแล้ว', color: '#3b82f6' },
archived: { label: 'เก็บไว้', color: '#94a3b8' },
} satisfies StatusConfig;
// ✓ TypeScript error ถ้าลืม key หรือใส่ key ผิด
// ✓ ยัง infer type ของ value ได้อย่างแม่นยำ
as const — Const Assertions
// ❌ ไม่มี as const: TypeScript widen ค่า
const direction = 'left'; // type: string
const directions = ['left', 'right']; // type: string[]
const config = { host: 'localhost', port: 3000 }; // { host: string; port: number }
// ✓ as const: freeze ทุกอย่างเป็น readonly literal
const direction = 'left' as const; // type: 'left'
const directions = ['left', 'right'] as const; // type: readonly ['left', 'right']
const config = { host: 'localhost', port: 3000 } as const;
// type: { readonly host: 'localhost'; readonly port: 3000 }
as const กับ Union Types
// สร้าง union type จาก array
const SIZES = ['sm', 'md', 'lg', 'xl'] as const;
type Size = (typeof SIZES)[number]; // 'sm' | 'md' | 'lg' | 'xl'
function resize(size: Size) { /* ... */ }
resize('md'); // ✓
resize('xxl'); // ❌ TypeScript error
// สร้าง object keys เป็น union
const HTTP_METHODS = {
GET: 'GET',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE',
} as const;
type HttpMethod = (typeof HTTP_METHODS)[keyof typeof HTTP_METHODS];
// 'GET' | 'POST' | 'PUT' | 'DELETE'
ใช้คู่กัน: satisfies + as const
type NavItem = { label: string; href: string; icon?: string };
const NAV = [
{ label: 'Projects', href: '/projects', icon: 'folder' },
{ label: 'Resources', href: '/resources', icon: 'book' },
{ label: 'About', href: '/about' },
] as const satisfies readonly NavItem[];
// ✓ validate ว่า match NavItem[]
// ✓ tuple type — รู้ว่า index 0 มี icon: 'folder' ไม่ใช่ string | undefined
NAV[0].icon; // 'folder' (literal)
NAV[2].href; // '/about' (literal)
const กับ Generic Functions
// ✓ ใช้ const type parameter (TS 5.0+)
function createRoute<const T extends string>(path: T) {
return { path, match: (url: string) => url === path };
}
const homeRoute = createRoute('/home');
homeRoute.path; // '/home' (literal) ไม่ใช่ string
เปรียบเทียบ
type annotation | as Type | satisfies Type | as const | |
|---|---|---|---|---|
| Validate | ✓ | ❌ | ✓ | — |
| Keep narrow | ❌ widen | ❌ widen | ✓ | ✓ |
| Readonly | ❌ | ❌ | ❌ | ✓ |
| ใช้เมื่อ | explicit type | cast | validate + narrow | freeze literals |