0Pricing
TypeScript Academy · บทเรียน

การตรวจสอบความครอบคลุมทั้งหมดด้วย never

ตรวจจับกรณีที่ไม่ได้จัดการตั้งแต่เวลาคอมไพล์ด้วย never

การตรวจสอบความครอบคลุมทั้งหมดด้วย never เป็นบทเรียน TypeScript Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน TypeScript Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

ปัญหาการจัดการให้ครบทุกกรณี

เมื่อคุณเพิ่มสมาชิกใหม่ในยูเนียน ก็อาจลืมจัดการสมาชิกนั้นในบางจุดได้ง่าย การตรวจสอบความครบถ้วนทุกกรณีจะเปลี่ยนการตกหล่นนั้นให้เป็นข้อผิดพลาดเวลาคอมไพล์

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

// If we add "triangle" later, we want every switch to complain.

ชนิดเนเวอร์

ชนิด never ใช้แทนค่าที่ไม่มีทางเกิดขึ้นได้ หากจัดการทุกกรณีแล้ว ค่าที่ไปถึงแขนงเริ่มต้นจะมีชนิดเป็น never

function fail(): never {
  throw new Error("unreachable");
}
// never is assignable to nothing except never itself.

การกำหนดค่าให้ never ในกรณีเริ่มต้น

ในแขนง default ให้กำหนดค่าให้ตัวแปรชนิด never หากจัดการสมาชิกทุกรูปแบบแล้ว การกำหนดค่านี้จะคอมไพล์ได้ แต่หากยังไม่ครบจะเกิดข้อผิดพลาด

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;
    case "square": return s.side ** 2;
    default:
      const _exhaustive: never = s;
      return _exhaustive;
  }
}
console.log(area({ kind: "square", side: 3 }));

จะเกิดอะไรขึ้นเมื่อลืมกรณีหนึ่ง

หากคุณเพิ่มสมาชิก triangle แต่ลืมเพิ่มกรณีของมัน s ในค่าเริ่มต้นจะไม่ใช่ never อีกต่อไป ดังนั้นการกำหนดค่าจะล้มเหลวในเวลาคอมไพล์

// type Shape = ... | { kind: "triangle"; base: number; height: number };
// Now in default, s is { kind: "triangle"; ... }
// const _exhaustive: never = s; // Error: triangle not assignable to never

ตัวช่วย assertNever

ฟังก์ชัน assertNever ที่นำกลับมาใช้ซ้ำได้จะรวมรูปแบบนี้ไว้ในจุดเดียว ฟังก์ชันนี้รับค่า never และโยนข้อผิดพลาด เพื่อบันทึกให้เห็นว่าแขนงดังกล่าวไม่ควรถูกเรียกใช้งาน

function assertNever(value: never): never {
  throw new Error("Unhandled case: " + JSON.stringify(value));
}
console.log(typeof assertNever);

การใช้ assertNever ในสวิตช์

เรียกใช้ assertNever(s) ในกรณีเริ่มต้น ฟังก์ชันนี้บังคับให้จัดการครบทุกกรณีในเวลาคอมไพล์ และให้ข้อผิดพลาดขณะทำงานที่ชัดเจนหากมีการเข้าถึงแขนงนี้

function assertNever(value: never): never {
  throw new Error("Unhandled: " + JSON.stringify(value));
}
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function area(s: Shape): number {
  switch (s.kind) {
    case "circle": return Math.PI * s.radius ** 2;
    case "square": return s.side ** 2;
    default: return assertNever(s);
  }
}
console.log(area({ kind: "circle", radius: 1 }).toFixed(2));

ความปลอดภัยเวลาคอมไพล์กับขณะทำงาน

การตรวจสอบด้วย never จะตรวจจับกรณีที่ขาดหายไปก่อนที่คุณจะเรียกใช้โค้ด และข้อผิดพลาดที่ถูกโยนจะช่วยป้องกันคุณหากมีบางอย่างหลุดผ่านการตรวจสอบชนิดไปได้ขณะทำงาน

function assertNever(x: never): never {
  throw new Error("Unhandled: " + String(x));
}
// Compile error if a case is missing; runtime throw as a backstop.
console.log("two layers of safety");

การตรวจสอบครบทุกกรณีโดยไม่ใช้ค่าเริ่มต้น

หากฟังก์ชันของคุณมีชนิดผลลัพธ์ที่ระบุไว้อย่างชัดเจน และสวิตช์ครอบคลุมทุกกรณี TypeScript ยังสามารถแจ้งเตือนเมื่อขาดการคืนค่าได้ด้วย ซึ่งเป็นการตรวจสอบความครบถ้วนอีกแบบหนึ่ง

type Light = "red" | "yellow" | "green";
function next(l: Light): Light {
  switch (l) {
    case "red": return "green";
    case "yellow": return "red";
    case "green": return "yellow";
  }
  // No default needed; all cases return.
}
console.log(next("red"));

การตรวจสอบครบทุกกรณีด้วยสายโซ่ if/else

แนวคิดเดียวกันนี้ใช้กับ if/else ได้เช่นกัน หลังจากจัดการสมาชิกแต่ละรูปแบบแล้ว else สุดท้ายจะได้รับค่า never

function assertNever(x: never): never { throw new Error("bad"); }
type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

function name(s: Shape): string {
  if (s.kind === "circle") return "circle";
  else if (s.kind === "square") return "square";
  else return assertNever(s);
}
console.log(name({ kind: "square", side: 2 }));

เหตุใด never จึงเป็นเครื่องมือที่เหมาะสม

เนื่องจากไม่สามารถกำหนดค่า never ให้ชนิดอื่นได้ สมาชิกที่เหลืออยู่จึงทำให้การกำหนดค่าล้มเหลว นี่ทำให้ never เป็นตัวตรวจจับกรณีที่ยังไม่ได้จัดการได้อย่างเหมาะสม

// Only never is assignable to never.
let x: never;
// x = "hi"; // Error
// Any concrete leftover type fails the same way.
console.log("never catches gaps");

ความครบถ้วนทุกกรณีในฐานะหลักประกันเมื่อปรับโครงสร้าง

เมื่อมี assertNever อยู่ทุกจุด การเพิ่มสมาชิกในยูเนียนจะสร้างรายการข้อผิดพลาดเวลาคอมไพล์ที่เป็นระเบียบ และชี้ตรงไปยังทุกจุดที่คุณต้องแก้ไข

function assertNever(x: never): never { throw new Error("unhandled"); }
type Status = "idle" | "busy";
function render(s: Status): string {
  switch (s) {
    case "idle": return "Idle";
    case "busy": return "Busy";
    default: return assertNever(s);
  }
}
console.log(render("idle"));

ตรวจสอบความเข้าใจ: ความครบถ้วนทุกกรณี

ตรวจสอบความเข้าใจของคุณเกี่ยวกับการตรวจสอบความครบถ้วนทุกกรณี

สรุปทบทวน: ความครบถ้วนทุกกรณีด้วยชนิดเนเวอร์

คุณได้เรียนรู้การกำหนดค่าเริ่มต้นให้เป็น never (หรือส่งค่าให้ assertNever) เพื่อให้คอมไพเลอร์บังคับให้คุณจัดการสมาชิกทุกรูปแบบ วิธีนี้เปลี่ยนกรณีที่ลืมจัดการให้กลายเป็นข้อผิดพลาดเวลาคอมไพล์

function assertNever(x: never): never { throw new Error("unhandled"); }
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function f(s: Shape) {
  switch (s.kind) {
    case "circle": return s.radius;
    case "square": return s.side;
    default: return assertNever(s);
  }
}
console.log(f({ kind: "circle", radius: 5 }));

คำถามที่พบบ่อย

บทเรียน “การตรวจสอบความครอบคลุมทั้งหมดด้วย never” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “การตรวจสอบความครอบคลุมทั้งหมดด้วย never” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส TypeScript Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส TypeScript Academy มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “การตรวจสอบความครอบคลุมทั้งหมดด้วย never”

ตรวจจับกรณีที่ไม่ได้จัดการตั้งแต่เวลาคอมไพล์ด้วย never คุณปฏิบัติ TypeScript Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน TypeScript Academy หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน TypeScript Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “การตรวจสอบความครอบคลุมทั้งหมดด้วย never” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน TypeScript Academy นี้ได้ไหม

ได้ บทเรียน TypeScript Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การสร้างยูเนียนแบบจำแนก
  2. การจำกัดชนิดข้อมูลด้วยตัวจำแนก
  3. การตรวจสอบความครอบคลุมทั้งหมดด้วย never
  4. การจำลองเครื่องสถานะ
← กลับไปที่ TypeScript Academy