Mapped Types
loop บน key
หัวข้อที่มีชื่อว่า “loop บน key”mapped type วนซ้ำบน key ของ type แล้วสร้าง type ใหม่ syntax คล้าย object type แต่ใส่ [K in ...] ตรงตำแหน่งที่ key ควรอยู่ — เป็น for loop ระดับ type
type Flags = { [K in "a" | "b" | "c"]: boolean;};// ^? { a: boolean; b: boolean; c: boolean }K ไล่ไปทีละ member ของ union "a" | "b" | "c" และสำหรับแต่ละตัว type ก็สร้าง property ออกมา รวมกับ keyof แล้วคุณวนซ้ำบน type ที่มีอยู่ได้:
interface User { id: number; name: string; }
type Stringify = { [K in keyof User]: string;};// ^? { id: string; name: string }อ้างถึง value type เดิม
หัวข้อที่มีชื่อว่า “อ้างถึง value type เดิม”ภายใน loop T[K] (indexed access) ให้ value type เดิม — คุณจึง transform แทนที่จะแทนที่ทั้งหมดได้:
type Getters<T> = { [K in keyof T]: () => T[K];};
type UserGetters = Getters<User>;// ^? { id: () => number; name: () => string }แต่ละ property กลายเป็น function ที่คืน value type นั้น รูปแบบ “เก็บ key ไว้ transform value” คือการใช้ mapped type ในชีวิตประจำวัน
mapping modifier: เพิ่มหรือลบ readonly และ ?
หัวข้อที่มีชื่อว่า “mapping modifier: เพิ่มหรือลบ readonly และ ?”mapped type เปลี่ยน modifier ของแต่ละ property ได้ด้วย คุณเพิ่ม readonly หรือ ? ด้วยการเขียนลงไปตรง ๆ และ ลบ ด้วย prefix -
// ทำให้ทุก property เป็น optional (นี่คือวิธีที่ Partial ทำงาน)type MyPartial<T> = { [K in keyof T]?: T[K];};
// ทำให้ทุก property เป็น readonly (นี่คือ Readonly)type MyReadonly<T> = { readonly [K in keyof T]: T[K];};
// ถอด optional และ readonly ออกจากทุก propertytype Concrete<T> = { -readonly [K in keyof T]-?: T[K];};flowchart LR
src["T: { id: number, name: string }"] -->|"for each K in keyof T"| loop["apply transform:
modifier + value type"]
loop --> out["new type:
{ id?: number, name?: string }"] -? ลบ optional และ -readonly ลบ immutability — นั่นคือวิธีที่ Required และ utility แบบ mutable-clone ถูกเขียนพอดี เวลาเห็น -? ใน library แปลว่า “บังคับให้ทุก property มีอยู่จริง”
key remapping ด้วย as
หัวข้อที่มีชื่อว่า “key remapping ด้วย as”feature ที่ใหม่กว่าให้คุณ rename หรือ filter key ระหว่าง map ด้วย clause as นี่คือวิธีสร้างอย่าง type “getters” ที่มีชื่อขึ้นต้น get หรือ drop key ที่ value type ไม่ต้องการ:
// rename แต่ละ key เป็น get<Key>type Getters2<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];};
type UG = Getters2<{ name: string }>;// ^? { getName: () => string }
// filter: เก็บเฉพาะ property ที่ value เป็น string (remap ตัวที่ไม่เอาเป็น never)type StringProps<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K];};การ remap key เป็น never จะ ลบ key นั้นออกจากผลลัพธ์ — เป็นทริกมาตรฐานสำหรับ filter property ตาม value type