วิธีสร้างประเภทแบบไดนามิกตามค่าด้วย 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;
}