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

วิธีสร้างประเภทแบบไดนามิกตามค่าด้วย Typescript


ฉันกำลังสร้างคอมโพเนนต์ React ที่จะมีแท็ก HTML แตกต่างกันตามคุณสมบัติที่ผู้ใช้ส่งมา นี่คือตัวอย่าง:

interface Common {
    common?: number;
}

interface A extends Common {
    first?: string;
    second?: string;
    check: true;
}

interface B extends Common {
    third?: string;
    fourth?: string;
    check?: false;
}

type Test = A | B;

เพื่อทดสอบสิ่งนี้ ฉันคาดหวังสิ่งต่อไปนี้:

// Success
let test: Test = {
    common: 1,
    check: true,
    first: "text"
}
// Success
let test: Test = {
    common: 1,
    check: false,
    third: "text"
}
// Fail
let test: Test = {
    common: 1,
    check: true,
    third: "text"
}
// Fail
let test: Test = {
    common: 1,
    check: false,
    first: "text"
}

ทั้งหมดนั้นทำงานได้ ความท้าทายคือการให้ได้ชนิด B โดยไม่ต้องส่งค่ามาตรวจสอบเลย เช่นนี้:

let test: Test = {
    common: 1,
    first: "text", // should highlight an error because `check` is not passed
    third: "text",
}

นี่คือสิ่งที่ฉันลองทำ:

// Attempt 1: is to include possible values
interface B extends Common {
    third?: string;
    fourth?: string;
    check?: false | null | undefined | never | unknown | void; // tried them all
}

// Attempt 2: is not to include at all. Still didn't work
interface B extends Common {
    third?: string;
    fourth?: string;
}

คำตอบ #1

นั่นเป็นเพราะ A | B เป็น OR แบบรวม แต่คุณต้องการ OR แบบเอ็กซ์คลูซีฟ คุณสามารถใช้แพ็กเกจ npm ts-xor ได้

คำตอบ #2

ไม่สามารถทำสิ่งนี้ได้แบบคงที่ คุณจำเป็นต้องใช้ตัวป้องกันประเภท (type guards) ดังนั้นคุณต้องมีชนิด Test ที่ถูกตรวจสอบแบบคงที่ก่อน แต่หลังจากนั้นคุณต้องตรวจสอบมันผ่านตัวป้องกันเพื่อให้ได้การพิมพ์ที่ถูกต้อง:

function isB(param: A | B): param is B {
  return (param as B).checked === false;
}

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