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

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
}