ข้ามไปยังเนื้อหา

The Utility Types

TypeScript มี standard library ของ utility type มาให้ ถึงตอนนี้คุณเห็นทุกเทคนิคที่ใช้แล้ว — แทนที่จะท่องจำ คุณจึง อ่าน utility เหล่านี้ออก นั่นคือเป้าหมายของบทเรียนนี้: จำแต่ละ utility ได้และรู้ primitive เบื้องหลัง

Utilityทำอะไรสร้างอย่างไร
Partial<T>ทุก property เป็น optionalmapped type กับ ?
Required<T>ทุก property requiredmapped type กับ -?
Readonly<T>ทุก property readonlymapped type กับ readonly
Pick<T, K>เก็บเฉพาะ key Kmapped type บน K + T[K]
Omit<T, K>drop key KPick ของ Exclude<keyof T, K>
Record<K, V>object ที่ key เป็น K, value เป็น Vmapped type บน K
Exclude<T, U>ลบ U ออกจาก union Tdistributive conditional
Extract<T, U>เก็บเฉพาะ U จาก union Tdistributive conditional
NonNullable<T>ลบ null/undefinedconditional type
ReturnType<F>return type ของ Fconditional กับ infer
Parameters<F>tuple ของ parameter ของ Fconditional กับ infer
Awaited<T>unwrap Promiserecursive conditional กับ infer

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 ของตัวเอง”

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>>

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>; // number
type 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"]
แต่ละ utility map ไปยัง primitive ที่คุณรู้แล้ว
`Partial<T>` ถูก implement อย่างไร?
เทคนิคใดที่เป็นพลังของ `Exclude<T, U>`?
`ReturnType<F>` ดึง return type อย่างไร?
`Omit<T, K>` มักถูก define อย่างไร?