typescript - ข้อผิดพลาดประเภทที่น่าประหลาดใจเมื่อพยายามอัปเดต union ที่มีแท็ก
ฉันคาดว่ารหัสด้านล่างนี้จะทำงานได้อย่างดี แต่บรรทัดที่ 7 เกิดข้อผิดพลาดพร้อมข้อความ Type '"one"' is not assignable to type '"two"'.:
type One = { type: 'one', value: number }
type Two = { type: 'two', value: number }
type OneOrTwo = One | Two
function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
const nextOneOrTwo: OneOrTwo = {
type: oneOrTwo.type,
value: oneOrTwo.value + 1,
}
return nextOneOrTwo
}
นี่ค่อนข้างน่าประหลาดใจ โดยเฉพาะอย่างยิ่งเมื่อฉันอัปเดตฟังก์ชันให้ใช้คำสั่งสวิตช์ (โดยมีฟังก์ชันการทำงานเหมือนกัน) มันกลับใช้ได้:
type One = { type: 'one', value: number }
type Two = { type: 'two', value: number }
type OneOrTwo = One | Two
function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
const nextOneOrTwo = {
value: oneOrTwo.value + 1,
}
switch (oneOrTwo.type) {
case 'one':
return { ...nextOneOrTwo, type: oneOrTwo.type }
case 'two':
return { ...nextOneOrTwo, type: oneOrTwo.type }
default:
throw unreachable(oneOrTwo)
}
}
function unreachable(value: never) {
return new Error(value)
}
แม้แต่การระบุชนิดก็ยังใช้ไม่ได้ (type: oneOrTwo.type as 'one' | 'two')
ฉันได้อ่านคำตอบเกี่ยวกับข้อผิดพลาด Type Error เมื่อพยายามแยกชนิดจาก union แล้ว แต่ฉันยังไม่แน่ใจว่าทำไมข้อผิดพลาดนี้ถึงเกิดขึ้นและจะแก้ไขอย่างไร
คำตอบ #1
สาเหตุที่คุณได้รับข้อผิดพลาดเป็นเพราะว่าเมื่อคุณเข้าถึง type ของยูเนียน มันคือ one | two แต่เมื่อคุณกำหนดค่าลิตเทอรัลของอ็อบเจ็กต์ วิธีการตรวจสอบคือถ้าลิตเทอรัลของอ็อบเจ็กต์ตรงกับสมาชิกอย่างน้อยหนึ่งของยูเนียน การกำหนดค่านั้นจะถูกต้อง
ดังนั้นเรามาดูกัน ประเภทของวัตถุ literal คือ { type : 'one' | 'two' value: number }.
ประเภทอ็อบเจ็กต์ลิเทอรัลเข้ากันได้กับ One หรือไม่? ไม่, type คือ 'one' | 'two' ในอ็อบเจ็กต์ลิเทอรัล แต่ใน One คือ 'one'
ประเภทอ็อบเจ็กต์ลิเทอรัลสามารถใช้ร่วมกับ Two ได้หรือไม่? ไม่, type คือ 'one' | 'two' ในอ็อบเจ็กต์ลิเทอรัล แต่เป็น 'two' ใน Two
ดังนั้นคุณจึงจบลงด้วยอ็อบเจ็กต์ลิเทอรัลที่ไม่สามารถกำหนดค่าให้กับสมาชิกใด ๆ ของยูเนียนได้
ตัวเลือกหนึ่งโดยไม่ต้องยืนยันชนิดคือการใช้การกระจายค่าพร้อมนิพจน์ ซึ่งจะรักษาชนิดสหสัมพันธ์เดิมไว้:
function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
const nextOneOrTwo: OneOrTwo = {
...oneOrTwo,
value: oneOrTwo.value + 1,
}
return nextOneOrTwo
}
หรือจะใช้การยืนยันประเภท:
function handleOneOrTwo(oneOrTwo: OneOrTwo): OneOrTwo {
const nextOneOrTwo: OneOrTwo = {
type: oneOrTwo.type,
value: oneOrTwo.value + 1,
} as OneOrTwo
return nextOneOrTwo
}
