keyof & Indexed Access
keyof ให้ key มาเป็น type
หัวข้อที่มีชื่อว่า “keyof ให้ key มาเป็น type”operator keyof รับ object type แล้วสร้าง union ของ key ออกมาเป็น type
interface User { id: number; name: string; active: boolean;}
type UserKey = keyof User;// ^? "id" | "name" | "active"keyof User ไม่ใช่ value — แต่เป็น union type "id" | "name" | "active" นี่คือวัตถุดิบสำหรับการวนซ้ำบน type ซึ่งก็คือสิ่งที่ mapped type ทำพอดี
สำหรับ object type ที่มี index signature keyof จะให้ index type ออกมา:
type Dict = { [key: string]: number };type DictKey = keyof Dict;// ^? string | number (JS object key เข้าถึงด้วย number ได้ด้วย)Indexed access: T[K] lookup value type
หัวข้อที่มีชื่อว่า “Indexed access: T[K] lookup value type”พอมี key แล้ว indexed access จะ lookup type ของ value ที่ key นั้น — เวอร์ชันระดับ type ของ obj[key]
interface User { id: number; name: string;}
type IdType = User["id"];// ^? number
type NameType = User["name"];// ^? stringindex ด้วย union ของ key เพื่อเอา union ของ value type ก็ได้:
type Values = User[keyof User];// ^? number | stringUser[keyof User] อ่านว่า “type ของทุก value ใน User” — เป็น idiom ที่ใช้บ่อยเพื่อเอา union ของ value type ทั้งหมดของ type
flowchart LR
t["User { id: number, name: string }"] -->|keyof| keys["keys: id or name"]
keys -->|"T[key]"| vals["value types: number or string"] index array และ tuple
หัวข้อที่มีชื่อว่า “index array และ tuple”array และ tuple ก็เป็น object type เหมือนกัน indexed access จึงใช้ได้ — และ [number] คือทริกสำหรับ “element type”
type Nums = number[];type Elem = Nums[number];// ^? number
const tuple = [1, "two", true] as const;type TupleElem = (typeof tuple)[number];// ^? 1 | "two" | true
type First = (typeof tuple)[0];// ^? 1Arr[number] แปลว่า “index array นี้ด้วย number อะไรก็ได้” ซึ่งได้ union ของ element type ทั้งหมด — เป็นวิธีมาตรฐานในการเปลี่ยน array/tuple type ให้เป็น union
ทำไมสองตัวนี้สำคัญมาก
หัวข้อที่มีชื่อว่า “ทำไมสองตัวนี้สำคัญมาก”แทบทุก utility type สร้างจาก keyof + indexed access + mapped type:
// Pick เขียนเอง — วนซ้ำ key ที่เลือก, lookup value type แต่ละตัวtype MyPick<T, K extends keyof T> = { [P in K]: T[P];};
type NameOnly = MyPick<User, "name">;// ^? { name: string }K extends keyof T จำกัด key ให้เป็น key จริงของ T; [P in K] วนซ้ำ key เหล่านั้น (mapped type, บทเรียนถัดไปถ้าคุณข้ามมา); T[P] lookup value type แต่ละตัว สาม primitive ได้เครื่องมือที่มีประโยชน์หนึ่งตัว