Back to notes
mastery-frontend-typescript

Control Flow Analysis: ศิลปะการควบคุมทิศทางข้อมูลใน TypeScript

มากกว่าแค่ if-else: ทำความเข้าใจว่า TypeScript Compiler คิดอย่างไรเมื่อเจอกับเงื่อนไข และการใช้ Type Predicates เพื่อช่วย Compiler

January 29, 20262 min read readNNexis by Seereen

บทนำ

Control Flow ในภาษาทั่วไปคือการกำหนดว่าโปรแกรมจะวิ่งไปทางไหน แต่ใน TypeScript มันมีหน้าที่พิเศษเพิ่มขึ้นมาคือ "Narrowing Types" (การบีบ Type ให้แคบลง) บทความนี้จะพาดูว่า Compiler "คิด" อย่างไรในแต่ละบรรทัด


1. Type Guard & Control Flow Analysis (CFA)

TypeScript ฉลาดพอที่จะอ่านโค้ดเราและรู้ว่า Type เปลี่ยนไปอย่างไรในแต่ละ Block

HLJS TYPESCRIPT
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

HLJS TYPESCRIPT
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)

HLJS TYPESCRIPT
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 ใหม่ๆ
Share this note

© 2026 My Notes by Seereen