บทนำ
Control Flow ในภาษาทั่วไปคือการกำหนดว่าโปรแกรมจะวิ่งไปทางไหน แต่ใน TypeScript มันมีหน้าที่พิเศษเพิ่มขึ้นมาคือ "Narrowing Types" (การบีบ Type ให้แคบลง) บทความนี้จะพาดูว่า Compiler "คิด" อย่างไรในแต่ละบรรทัด
1. Type Guard & Control Flow Analysis (CFA)
TypeScript ฉลาดพอที่จะอ่านโค้ดเราและรู้ว่า Type เปลี่ยนไปอย่างไรในแต่ละ Block
function padLeft(padding: number | string, input: string) {
// ณ จุดนี้ padding คือ number | string
if (typeof padding === "number") {
// ✨ CFA ทำงาน: ในบล็อกนี้ TS รู้ว่า padding เป็น number แน่นอน
return " ".repeat(padding) + input;
}
// ✨ CFA ฉลาดต่อ: ถ้าหลุดมาถึงตรงนี้ได้ แปลว่า padding *ต้อง* เป็น string เท่านั้น
// (เพราะ number return ไปแล้ว)
return padding + input;
}
Deep Dive: กลไกนี้เรียกว่า Reachability Analysis Compiler จะสร้าง Graph ของความเป็นไปได้ และตัด (Prune) branches ที่เป็นไปไม่ได้ทิ้ง
2. User-Defined Type Guards (Type Predicates)
บางครั้ง Logic การเช็ค Type ซับซ้อนเกินกว่าที่ TS จะเข้าใจเอง เราต้องเขียนฟังก์ชันช่วย โดยใช้ syntax arg is Type
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
// Return type พิเศษ: "pet is Fish"
function isFish(pet: Fish | Bird): pet is Fish {
return (pet as Fish).swim !== undefined;
}
function move(pet: Fish | Bird) {
if (isFish(pet)) {
// TS รู้ทันทีว่า pet คือ Fish
pet.swim();
} else {
// TS รู้ทันทีว่า pet คือ Bird (เพราะไม่ใช่ Fish)
pet.fly();
}
}
ข้อควรระวัง: is ... คือการบอก Compiler ให้ "เชื่อฉัน" ถ้า Logic ข้างใน isFish ผิด (เช่น return true มั่วๆ) TS ก็จะเชื่อจนหมดใจ ทำให้เกิด Runtime Error ได้
3. Exhaustiveness Checking (The never type)
ใช้ never เพื่อตรวจสอบว่าเราดักเคสครบทุกทางหรือยัง (มีประโยชน์มากใน Switch case)
type Shape = "circle" | "square" | "triangle";
function getArea(s: Shape) {
switch (s) {
case "circle":
return /*...*/;
case "square":
return /*...*/;
// ลืม case "triangle"!
default:
// ❌ Error: Type 'string' is not assignable to type 'never'.
// เพราะ s ยังเหลือเคส triangle อยู่ มันเลยไม่เป็น never
const _exhaustiveCheck: never = s;
return _exhaustiveCheck;
}
}
เทคนิคนี้ช่วยให้เมื่อเราเพิ่มชนิดข้อมูลใหม่ (เช่น "triangle") โค้ดส่วนอื่นๆ ที่เกี่ยวข้องจะ แดง ขึ้นมาทันที ช่วยกันลืมแก้โค้ดได้ดีเยี่ยม
สรุป
Control Flow ใน TypeScript ไม่ใช่แค่เรื่อง Logic การทำงาน แต่เป็นเครื่องมือสื่อสารกับ Compiler
- ใช้
if/switchพื้นฐานเพื่อ Narrow Type - ใช้ Type Predicate (
is) เมื่อ Logic ซับซ้อน - ใช้ Exhaustiveness Check (
never) เพื่อกันลืม Handle case ใหม่ๆ