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

ยูเนียนที่แยกความแตกต่างด้วยบูลีนใน Typescript


เพื่อติดตามวัตถุที่กำลังเริ่มต้นหรือได้เริ่มต้นแล้ว ฉันต้องการสร้าง union ที่มีการจำแนกด้วยค่าบูลีน ดังนั้นฉันจึงเขียนโค้ดดังต่อไปนี้:

interface InitializingThing extends BaseThing {
    initialized: false;
    value: undefined;
}

interface InitializedThing extends BaseThing {
    initialized: true;
    value: number;
}

type Thing = InitializingThing | InitializedThing;

const thing: Thing = { initialized: false, value: undefined };
console.log(thing);

getThing().then((value: number) => {
    thing.value = value;
    thing.initialized = true;
}).then(() => {
    if (!thing.initialized) {
        return;
    }
    console.log(15 + thing.value);
});

(ดูได้ที่ Typescript playground)

อย่างไรก็ตาม นี่ทำให้เกิดข้อผิดพลาด

Type 'number' is not assignable to type 'undefined'.(2322)
Type 'true' is not assignable to type 'false'.(2322)

ฉันสามารถเห็นได้จากการวางเมาส์เหนือ console.log(thing) ว่าประเภทคือ InitializingThing แทนที่จะเป็น Thing! ซึ่งดูเหมือนจะเป็นต้นเหตุของปัญหา แต่ฉันไม่แน่ใจว่าทำไมคอมไพเลอร์ TS ถึงทำแบบนี้

คำตอบ #1

โค้ดของคุณโดยหลักการทำงานได้ดี อย่างไรก็ตาม ในตัวอย่างข้างต้น TS แท้จริงแล้วฉลาดเกินไปเล็กน้อยกับการวิเคราะห์การไหลของการควบคุม ในการกำหนดค่าตัวแปรดังต่อไปนี้

const thing: Thing = { initialized: false, value: undefined };

, ตัวคอมไพเลอร์จะลดประเภทของ thing เป็น InitializingThing โดยการตีความตัวเริ่มต้นของมัน thing ยังถือว่ารักษาประเภทนี้ไว้ เนื่องจากตัวแปรเป็นแบบอ่านอย่างเดียว/const นั่นคือเหตุผลว่าทำไมจึงเกิดข้อผิดพลาดสำหรับการกำหนดค่าใหม่ในคำสั่ง then

ถ้าคุณบังคับให้ thing เป็นประเภท Thing จริง ๆ ตัวอย่างที่ให้มาจะสามารถคอมไพล์ได้อีกครั้ง:

const thing: Thing = { initialized: false, value: undefined } as Thing; // note the cast here
// or 
let thing: Thing = { initialized: false, value: undefined }; // switch to mutable `let`

สนามเด็กเล่น

คำตอบ #2

ที่บรรทัดนี้ของโค้ด วัตถุไม่ถูกต้อง:

thing.value = value;

ตอนนี้มันมีประเภท {initialized: false, value: number} ซึ่งไม่ถูกต้อง

แทนที่จะเป็นเช่นนั้น คุณจำเป็นต้องเปลี่ยนค่าพร้อมกัน:

thing = { value: value, initialized, true }

แต่ในการทำเช่นนั้น คุณจะพบว่า thing ไม่สามารถเป็น const ได้ (เพราะคุณกำลังกำหนดค่าของมันใหม่) คุณจะต้องทำให้มันเป็น let

บทความที่เกี่ยวข้อง