Unions & Intersections
Union: value ที่เป็นหนึ่งในหลาย type
หัวข้อที่มีชื่อว่า “Union: value ที่เป็นหนึ่งในหลาย type”union A | B อธิบาย value ที่เป็น ไม่ A ก็ B ใน model เซตนี่คือ set union — union จึง กว้างกว่า สมาชิกของตัวเองเสมอ
type Id = string | number;
let id: Id = "abc";id = 123; // ✅ ทั้งคู่เป็นสมาชิกของ unionจุดที่ต้องระวัง: ระหว่างที่คุณถือ union อยู่ คุณแตะได้แค่ member ที่ ทุก branch มีร่วมกัน เพราะ compiler ยังไม่รู้ว่าคุณถือตัวไหน
function printId(id: string | number) { id.toUpperCase(); // ❌ toUpperCase ไม่มีบน number // ตรงนี้ใช้ได้แค่ member ที่ string และ number มีร่วมกัน}จะใช้ member เฉพาะ type ต้อง narrow ก่อน — เนื้อหาทั้งบทหน้า จนกว่าจะ narrow union จะเปิดให้เห็นแค่ surface ที่มีร่วมกัน
Intersection: value ที่เป็นทุก type พร้อมกัน
หัวข้อที่มีชื่อว่า “Intersection: value ที่เป็นทุก type พร้อมกัน”intersection A & B อธิบาย value ที่เป็นทั้ง A และ B พร้อมกัน สำหรับ object type นี่หมายถึง “มีทุก property ของทั้งสอง” — intersection ของ object จึง แคบกว่า (เงื่อนไขมากขึ้น value ที่ valid น้อยลง)
type WithId = { id: string };type WithTimestamps = { createdAt: Date; updatedAt: Date };
type Entity = WithId & WithTimestamps;// ต้องมี id และ createdAt และ updatedAt
const e: Entity = { id: "1", createdAt: new Date(), updatedAt: new Date() };นี่คือวิธี idiomatic ในการประกอบ object type จากชิ้นเล็ก ๆ ที่ reuse ได้ — ยืดหยุ่นกว่า inheritance มาก เพราะผสมได้ทุกแบบ
ทั้งสองดึงไปคนละทาง
หัวข้อที่มีชื่อว่า “ทั้งสองดึงไปคนละทาง”flowchart TB
subgraph un["A OR B (union)"]
u["more values allowed
fewer members guaranteed"]
end
subgraph inter["A AND B (intersection)"]
i["fewer values allowed
more members guaranteed"]
end สังเกต trade-off ของแต่ละทิศ:
- union อนุญาต value มากขึ้น แต่การันตี member ที่แตะได้อย่างปลอดภัยน้อยลง (แค่ที่มีร่วมกัน)
- intersection อนุญาต value น้อยลง แต่การันตี member มากขึ้น (ครบทุกตัว)
edge case หลุดออกมาจาก model เซตเอง
หัวข้อที่มีชื่อว่า “edge case หลุดออกมาจาก model เซตเอง”string & numberเป็นnever— ไม่มี value ไหนเป็นทั้งสอง intersection เลยเป็นเซตว่าง- union กับ
anyจะยุบเป็นany; union กับneverจะทิ้งneverไป (T | neverก็คือTเพราะการเพิ่มเซตว่างไม่เปลี่ยนอะไร) - intersect object type ที่มี property primitive ชนกัน อาจได้
neverสำหรับ property นั้น
type A = { kind: "a"; value: number };type B = { kind: "b"; value: string };type Both = A & B;// kind: "a" & "b" → never; type นี้สร้างจริงไม่ได้ตัวสุดท้ายเป็นสัญญาณ: เวลาคุณอยากได้ “หนึ่งใน object shape พวกนี้” คุณเกือบทุกครั้งต้องการ union (A | B) ที่มี discriminant field ร่วมกัน — ไม่ใช่ intersection นั่นคือ discriminated-union pattern ในบท narrowing