typescript - ทำไมการใช้ตัวดำเนินการตามเงื่อนไขจึงส่งผลให้เกิดประเภทอินเทอร์เซ็กชัน?
ฉันกำลังพยายามสร้างฟังก์ชันทั่วไปที่คืนค่าตามเงื่อนไขโดยขึ้นอยู่กับประเภทของอาร์กิวเมนต์ของมัน แต่ฉันติดขัดในการพยายามใช้งานประเภทการคืนค่า
สมมติว่ามีประเภท Basket
type Basket = {
Fruit: 'banana',
Veggie: 'tomato'
}
ตอนนี้ถ้าฉันต้องการส่งค่ากลับแบบมีเงื่อนไขระหว่าง 'กล้วย' หรือ 'มะเขือเทศ' ขึ้นอยู่กับอาร์กิวเมนต์ที่ส่งไปยังฟังก์ชัน ฉันไม่สามารถทำให้มันคอมไพล์ได้:
const f1 = <T extends keyof Basket>(t: T): T extends 'Fruit'? 'banana': 'tomato' => {
if (t == 'Fruit') {
return 'banana' //Error on this line - doesn't compile -- Type '"banana"' is not assignable to type '"banana" & "tomato"
} else {
return 'tomato' //Error on this line - doesn't compile -- Type '"tomato"' is not assignable to type '"banana" & "tomato"
}
}
ตอนนี้เมื่อฉันสร้างอินสแตนซ์ของสิ่งนี้หลังจากส่งพารามิเตอร์เจเนอริกที่ถูกต้อง ฉันได้รับชนิดที่ฉันคาดหวัง แต่โค้ดไม่สามารถคอมไพล์ได้
const doesntCompile: 'banana' = f1<'Fruit'>('') //type: 'banana', which is what I want ... but this doesn't compile due to the error above.
อย่างไรก็ตาม ถ้าฉันไม่ใช้เจเนอริก ฉันจะได้ชนิดผลรวมดังนี้
//This compiles
const f2 = <T extends keyof Basket>(t: string): Basket[keyof Basket] => { //return type is 'banana' | 'tomato'
if (t == 'yellow') {
return 'banana'
} else {
return 'tomato'
}
}
const complies: 'banana' | 'tomato' = f2<'Fruit'>('') //type: 'banana' | 'tomato', but I want the type here to be 'banana'
ตอนนี้มันคอมไพล์ได้เรียบร้อย แต่ฉันสูญเสียประโยชน์ของความปลอดภัยของประเภทข้อมูล
ฉันจะทำให้ตัวอย่างนี้ทำงานได้อย่างไรในขณะที่ยังคงรักษา Generics อยู่? ความช่วยเหลือใด ๆ จะขอบคุณอย่างมาก
คำตอบ #1
เกี่ยวกับปัญหาของคุณ มันมาจากชนิดเงื่อนไขที่ถูกเลื่อนออกไป ดูเอกสารของ TypeScript: https://www.typescriptlang.org/docs/handbook/advanced-types.html#conditional-types. (ค้นหาคำว่า conditional types are deferred เพื่อไปยังตำแหน่งที่ถูกต้องในหน้า).
วิธีแก้ปัญหาที่ง่ายที่สุดคือการใช้ลายเซ็นการใช้งานแยกต่างหากที่อนุญาตมากกว่า ในขณะที่ยังคงลายเซ็นสาธารณะที่มีประเภทตามเงื่อนไขซึ่งเหมาะสมกว่าสำหรับผู้เรียก:
type Basket = {
Fruit: 'banana',
Veggie: 'tomato'
}
function f3 <T extends keyof Basket>(t: T): Basket[T];
function f3 <T extends keyof Basket>(t: string): Basket[keyof Basket] {
if (t == 'Fruit') {
return 'banana'
} else {
return 'tomato'
}
}
const complies2 = f3('Fruit'); // complies2 is "banana"
หมายเหตุ: ฟังก์ชันลูกศรไม่สามารถใช้กับการโอเวอร์โหลดฟังก์ชันได้
