Literals & Widening
literal คือเซตที่มีสมาชิกตัวเดียว
หัวข้อที่มีชื่อว่า “literal คือเซตที่มีสมาชิกตัวเดียว”literal type คือ type ที่มี value ได้ค่าเดียวคือ literal ที่ระบุ "GET" เป็น type และ value เดียวที่ assign ให้ type นี้ได้คือ string "GET" เช่นเดียวกับ 42, true และอื่น ๆ
let method: "GET" = "GET";method = "POST"; // ❌ Type '"POST"' is not assignable to type '"GET"'.ลำพัง literal type แทบไม่มีประโยชน์ พลังของตัวเองมาจาก union ของ literal ซึ่ง model ความหมาย “หนึ่งในค่าที่ระบุพวกนี้”:
type Method = "GET" | "POST" | "PUT" | "DELETE";type Dice = 1 | 2 | 3 | 4 | 5 | 6;นี่คือวิธีแทน parameter แบบ string หลวม ๆ ด้วยตัวที่แม่นยำ อธิบายตัวเองได้ และ autocomplete ได้
Widening: ทำไม literal ถึงหายไปเรื่อย ๆ
หัวข้อที่มีชื่อว่า “Widening: ทำไม literal ถึงหายไปเรื่อย ๆ”TypeScript จะ widen literal ไปเป็น type ทั่วไปในที่ที่ value อาจเปลี่ยนได้ กฎที่คุณเจอใน Foundations: const เก็บ literal, let widen
const a = "GET"; // type: "GET"let b = "GET"; // type: string (widen — b reassign ได้)widening แบบเดียวกันเกิดกับ property ของ object ด้วย เพราะ property ของ object แก้ค่าได้โดย default:
const config = { method: "GET" };// type: { method: string } — ไม่ใช่ { method: "GET" }
function send(m: "GET" | "POST") {}send(config.method); // ❌ string assign ให้ "GET" | "POST" ไม่ได้ถึง config จะเป็น const แต่ property method ยัง reassign ได้ (config.method = "POST" ทำได้) compiler เลย widen เป็น string จุดนี้ทำทุกคนงงครั้งหนึ่งเสมอ
as const: ล็อก type แบบแคบไว้
หัวข้อที่มีชื่อว่า “as const: ล็อก type แบบแคบไว้”const assertion (as const) บอก compiler ว่า “มองค่านี้เป็น immutable แบบลึก และเก็บทุก literal ให้แคบไว้”
const config = { method: "GET" } as const;// type: { readonly method: "GET" }
send(config.method); // ✅ ตอนนี้ "GET" อยู่รอดas const ทำ 3 อย่างพร้อมกัน:
- literal ยังเป็น literal —
"GET"แทนstring - property กลายเป็น
readonly— สะท้อน immutability ที่คุณสัญญาไว้ - array กลายเป็น
readonlytuple —[1, 2]กลายเป็นreadonly [1, 2]ไม่ใช่number[]
flowchart TB
val["value: { method: 'GET' }"] -->|default| wide["type: { method: string }
(widened, mutable)"]
val -->|as const| narrow["type: { readonly method: 'GET' }
(literal, readonly)"] use case ที่พบบ่อยมาก: literal config object
หัวข้อที่มีชื่อว่า “use case ที่พบบ่อยมาก: literal config object”as const เปล่งประกายกับ lookup table และ config ที่คุณอยากได้ key/value เป๊ะ ๆ ใน type:
const ROUTES = { home: "/", profile: "/profile", settings: "/settings",} as const;
type Route = typeof ROUTES[keyof typeof ROUTES];// type: "/" | "/profile" | "/settings"ถ้าไม่มี as const ROUTES จะเป็น { home: string; profile: string; settings: string } แล้วคุณก็เสีย path เฉพาะทั้งหมด แต่พอมี คุณ derive union type ที่แม่นยำจากตัวข้อมูลได้ตรง ๆ — pattern ที่คุณจะเจอตลอดในโมดูล type-level