The Utility Types
คุณรู้อยู่แล้วว่าพวกนี้ถูกสร้างยังไง
หัวข้อที่มีชื่อว่า “คุณรู้อยู่แล้วว่าพวกนี้ถูกสร้างยังไง”TypeScript มี standard library ของ utility type มาให้ ถึงตอนนี้คุณเห็นทุกเทคนิคที่ใช้แล้ว — แทนที่จะท่องจำ คุณจึง อ่าน utility เหล่านี้ออก นั่นคือเป้าหมายของบทเรียนนี้: จำแต่ละ utility ได้และรู้ primitive เบื้องหลัง
| Utility | ทำอะไร | สร้างอย่างไร |
|---|---|---|
Partial<T> | ทุก property เป็น optional | mapped type กับ ? |
Required<T> | ทุก property required | mapped type กับ -? |
Readonly<T> | ทุก property readonly | mapped type กับ readonly |
Pick<T, K> | เก็บเฉพาะ key K | mapped type บน K + T[K] |
Omit<T, K> | drop key K | Pick ของ Exclude<keyof T, K> |
Record<K, V> | object ที่ key เป็น K, value เป็น V | mapped type บน K |
Exclude<T, U> | ลบ U ออกจาก union T | distributive conditional |
Extract<T, U> | เก็บเฉพาะ U จาก union T | distributive conditional |
NonNullable<T> | ลบ null/undefined | conditional type |
ReturnType<F> | return type ของ F | conditional กับ infer |
Parameters<F> | tuple ของ parameter ของ F | conditional กับ infer |
Awaited<T> | unwrap Promise | recursive conditional กับ infer |
ตระกูล mapped-type
หัวข้อที่มีชื่อว่า “ตระกูล mapped-type”Partial, Required, Readonly, Pick และ Record ล้วนเป็น mapped type บรรทัดเดียว:
type Partial<T> = { [K in keyof T]?: T[K] };type Required<T> = { [K in keyof T]-?: T[K] };type Readonly<T> = { readonly [K in keyof T]: T[K] };type Pick<T, K extends keyof T> = { [P in K]: T[P] };type Record<K extends keyof any, V> = { [P in K]: V };ไม่มีอะไรพิสดาร — loop บน key บางทีเปลี่ยน modifier บางที lookup T[K] พอเห็นแบบนี้ Pick<User, "id" | "name"> ก็ชัดว่าคือ “วนซ้ำสอง key นั้น copy value type ของตัวเอง”
ตระกูล distributive-conditional
หัวข้อที่มีชื่อว่า “ตระกูล distributive-conditional”Exclude, Extract และ NonNullable ทำงานบน union ด้วย conditional type ที่ distribute บนแต่ละ member:
type Exclude<T, U> = T extends U ? never : T;type Extract<T, U> = T extends U ? T : never;type NonNullable<T> = T extends null | undefined ? never : T;
type A = Exclude<"a" | "b" | "c", "b">; // "a" | "c"เมื่อ type ที่ถูกเช็คเป็น naked type parameter และคุณส่ง union เข้าไป conditional จะรัน ทีละ member แล้ว union ผลลัพธ์ ดังนั้น Exclude map แต่ละ member เป็น never (drop) หรือตัวเอง (เก็บ) — และ never หายไปจาก union Omit จึงตกมาเป็น Pick<T, Exclude<keyof T, K>>
ตระกูล infer
หัวข้อที่มีชื่อว่า “ตระกูล infer”ReturnType, Parameters และ Awaited capture type ออกมาจาก structure ด้วย infer:
type ReturnType<F> = F extends (...args: any[]) => infer R ? R : never;type Parameters<F> = F extends (...args: infer P) => any ? P : never;
type R = ReturnType<() => number>; // numbertype P = Parameters<(a: string, b: number) => void>; // [string, number]infer R match ตำแหน่ง return ของ function แล้ว bind เข้า R; infer P match parameter list ทั้งหมดเป็น tuple Awaited เพิ่ม recursion เพื่อลอก promise ที่ซ้อนกัน
flowchart TB mapped["Mapped types"] --> f1["Partial, Required, Readonly, Pick, Record"] cond["Distributive conditionals"] --> f2["Exclude, Extract, NonNullable, Omit"] infer["Conditionals with infer"] --> f3["ReturnType, Parameters, Awaited"]