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

The Type-System Mindset

นี่คือการปรับมุมมองที่ปลดล็อก TypeScript เลิกคิดว่า type เป็น ป้ายชื่อ แล้วเริ่มคิดว่าเป็น เซต — คือกลุ่มของ value ทุกตัวที่เป็นสมาชิกของตัวเองอย่างถูกต้อง

  • boolean คือเซต { true, false } — มีสมาชิกสองตัวพอดี
  • undefined คือเซตที่มีสมาชิก หนึ่ง ตัว: { undefined }
  • string คือเซต (อนันต์) ของ string ทั้งหมด
  • 42 — ใช่ literal — คือเซตที่มีสมาชิก หนึ่ง ตัว: { 42 } นี่คือ literal type

พอ type กลายเป็นเซต operation ที่คุณรู้จากคณิตศาสตร์อยู่แล้วก็อธิบายทั้ง system ได้

a assign ให้ b ได้ ก็ต่อเมื่อ เซต a เป็น subset ของเซต b นั่นคือกฎทั้งหมด

let x: 42 = 42;
let y: number = x; // ✅ {42} ⊆ all numbers
let a: number = 42;
let b: 42 = a; // ❌ all numbers ⊄ {42}
// Error: Type 'number' is not assignable to type '42'.

literal 42 พอดีเข้าไปใน number ได้เพราะ {42} เป็น subset ของทุกจำนวน ทางกลับกันพังเพราะจำนวนส่วนใหญ่ไม่ใช่ 42 คุณไม่ต้องท่องกฎ assignability เลย — แค่ถามว่า “เซตนี้อยู่ในเซตนั้นไหม”

flowchart TB
  subgraph u["A หรือ B  (union = OR)"]
    ua["string"] --- ub["number"]
    note1["value ที่อยู่ในเซตใดเซตหนึ่ง
ยิ่งมี value เยอะ ยิ่งกว้าง"]
  end
  subgraph i["A และ B  (intersection = AND)"]
    note2["value ที่อยู่ในทั้งสองเซต
ยิ่งมี value น้อย ยิ่งแคบ"]
  end
type ในฐานะเซต: union ทำให้กว้างขึ้น intersection ทำให้แคบลง
  • string | number คือ union ของสองเซต — value ที่อยู่ใน เซตใดเซตหนึ่ง union จะ กว้างกว่า (สมาชิกเยอะกว่า)
  • A & B คือ intersection — value ที่อยู่ใน ทั้งสอง สำหรับ object type แปลว่า “มี property ของทั้งสองครบ” นั่นคือเหตุผลที่ intersection ทำให้ แคบลง

เรื่องน่าประหลาดใจที่พบบ่อยหลุดออกมาจากตรงนี้: string & number คือเซตว่าง ไม่มี value ไหนเป็นทั้ง string และ number — type ของตัวเองจึงเป็น never

เซตมีตัวเล็กสุดและใหญ่สุด และ TypeScript ตั้งชื่อให้:

  • never คือ เซตว่าง { } — ไม่มี value เลย ไม่มีอะไร assign ให้ never ได้ (คุณสร้างสมาชิกของเซตว่างไม่ได้) แต่ never assign ให้ ทุกอย่าง ได้ (เซตว่างเป็น subset ของทุกเซต) never คือ type ของ function ที่ไม่มีทาง return และเป็นสัญญาณว่า branch นั้นไปไม่ถึง
  • unknown คือ เซตของ value ทั้งหมด — top type ทุกอย่าง assign ให้ unknown ได้ แต่ unknown assign ให้แทบไม่มีอะไรได้จนกว่าคุณจะ narrow ก่อน พูดง่าย ๆ unknown คือ any เวอร์ชันที่ปลอดภัยเรื่อง type
let n: never;
let u: unknown = "anything goes";
let s: string = u; // ❌ unknown is too wide — narrow it first
let x: unknown = 42; // ✅ everything fits into unknown

any ทำลาย model นี้โดยตั้งใจ: เป็นการขอ ออก จากการเช็คเซตทั้งหมด นั่นคือเหตุผลที่อันตราย ส่วน unknown รักษา model ไว้และบังคับให้คุณ narrow

ใน model "type คือเซต" เมื่อไรที่ type A assign ให้ type B ได้?
type ของ `string & number` คืออะไร และเพราะอะไร?
`never` แทนเซตอะไร?
`unknown` ต่างจาก `any` อย่างไร?