ค้นหาเว็บไซต์

ค่าที่ตรวจสอบใน TypeScript ยังคงถูกทำเครื่องหมายว่าเป็น undefined ในตัวฟังก์ชัน


มีการตรวจสอบ if บน var1 และสิ่งนี้ทำงานตามที่คาดไว้ ยกเว้นตัวฟังก์ชันภายในคำสั่ง if ไม่ชัดเจนว่าเพราะอะไร

let var1: number | undefined;
let var2: number;

if (var1) {
  var2 = var1;
  const func = () => {
    var2 = var1;
  };
  func();
  console.log(var2);
}

คำตอบ #1

คำตอบมาตรฐานสำหรับคำถามนี้สามารถพบได้ที่ microsoft/TypeScript#9998

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

if (var1) {
    var2 = var1;
    // replace func() with its body
    {
        var2 = var1;
    }
    console.log(var2);
}

แต่การทำเช่นนี้ตลอดเวลา "คงไม่ใช่เรื่องที่ปฏิบัติได้แม้แต่น้อย" ตามที่กล่าวไว้ใน microsoft/TypeScript#9998 แทนที่นั้น ตัวคอมไพเลอร์ต้องใช้ทางลัดเพื่อให้ทำงานได้มีประสิทธิภาพ และทางลัดเหล่านั้นมักจะทำให้เกิดผลลบเท็จ (ไม่มีการรายงานข้อผิดพลาดแม้ว่าจะมีปัญหาจริง) ผลบวกเท็จ (รายงานข้อผิดพลาดแม้ว่าไม่มีปัญหาจริง) หรือทั้งสองอย่าง

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

if (var1) {
    const func = () => (var1 = undefined);
    func();
    var2 = var1; // no error, but there should be
}

และสมมติอย่างเชิงลบว่าไม่มีการจำกัดลงภายในร่างกายของฟังก์ชัน ซึ่งนำไปสู่ผลบวกเทียม เช่นเดียวกับในตัวอย่างโค้ดของคุณ:

if (var1) {
    const func = () => { 
        var2 = var1; // error, but there shouldn't be
    }; 
    func();
}

มีการอภิปรายใน microsoft/TypeScript#9998 เกี่ยวกับแนวทางที่เป็นไปได้ต่าง ๆ และว่าพวกมันนำไปสู่ปัญหาในสถานการณ์อื่น ๆ อย่างไร สำหรับตอนนี้ สิ่งนี้ก็เป็นเพียงสิ่งที่เป็นอยู่


แล้ววิธีแก้ปัญหาที่นี่คืออะไร? ชัดเจนว่าคุณสามารถทำการ inline func() ด้วยตัวเอง อีกวิธีหนึ่งคือการเลี่ยงปัญหาโดยการบันทึกค่าที่ถูกจำกัดประเภทแล้ว var1 ลงในตัวแปรใหม่ (เช่น var1Saved) ซึ่งคอมไพเลอร์จะสรุปให้มีประเภทจำกัดตามค่าที่เริ่มต้นด้วย ประเภทดังกล่าวจะไม่สามารถขยายประเภทได้ ดังนั้นมันจะถูกมองว่าเป็น number เสมอและจะไม่เคยเป็น number | undefined แม้กระทั่งใน func():

if (var1) {
    const var1Saved = var1;
    const func = () => {
        var2 = var1Saved; // okay
    };
    func();
}

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

if (var1) {
    const func = () => {
        var2 = var1 as number; // okay
    };
    func();
}

ลิงก์ Playground ไปยังโค้ด

คำตอบ #2

let var1: number | undefined
let var2: number | undefined

if (var1) {
    var2 = var1;
    const func = () => {
        var2 = var1;
    }
    func();
    console.log(var2)
}

เมื่อค่าของ var2 รับค่าของ var1 หมายความว่าในบางจุดมันอาจไม่ถูกกำหนด ดังนั้นคุณจึงหลีกเลี่ยงปัญหาประเภทนี้