javascript - Typescript union กับประเภทที่ขึ้นต่อกัน
ถ้าฉันมียูเนียนดังต่อไปนี้ที่ห่อหุ้มฟังก์ชันและอาร์กิวเมนต์ของมัน ฉันจะเรียกใช้มันได้อย่างไร?
type Wrapper = {
fn: (a: string) => void
arg: string
} | {
fn: (a: number) => void
arg: number
}
let foo!: Wrapper
foo.fn(foo.arg) // Error Type 'string' is not assignable to type 'never'.
ฉันไม่สามารถหาวิธีเรียกมันได้ ทุกอย่างที่ฉันลองโดยพื้นฐานแล้วลงเอยที่การคาสต์ (เช่น การคาสต์เป็น (a:any) => void) ซึ่งฉันสามารถทำได้ถ้าจำเป็น แต่ฉันรู้สึกว่าฉันไม่ควรต้องทำแบบนั้น
สามารถเรียกฟังก์ชันนี้โดยไม่ต้องแปลงประเภทได้หรือไม่?
แก้ไข: เพื่อชี้แจง ฉันกำลังถามว่ามีวิธีแก้ปัญหาที่ไม่เกี่ยวข้องกับการเปลี่ยนแปลงคำนิยามของ Wrapper หรือไม่
คำตอบ #1
คุณสามารถใช้ชนิดแบบมีเงื่อนไขเพื่อทำให้ประเภทไม่กำกวม น่าเสียดายที่มันทำให้คุณต้องทำซ้ำประเภทยูเนียนของตัวห่ออีกครั้ง และถ้าคุณมีสิ่งที่ต้องตรวจสอบมากขึ้น ชนิดแบบมีเงื่อนไขของคุณจะดูน่าเกลียด แต่ก็ทำให้คุณหลีกเลี่ยงการแปลงประเภท (ในบางแง่) ได้
type Wrapper = {
fn: (a: string) => void
arg: string
} | {
fn: (a: number) => void
arg: number
}
type Disambiguate<W extends Wrapper> = W['arg'] extends string
? { fn: (a : string) => void, arg: string }
: { fn: (a: number) => void, arg: number };
let foo!: Disambiguate<Wrapper>
// The compiler is satisfied. No error here.
// foo can only be one of the branches in the conditional type.
foo.fn(foo.arg);
คำตอบ #2
ไม่แน่ใจนักเกี่ยวกับกรณีการใช้งานของคุณ แต่สิ่งด้านบนสามารถทำได้โดยใช้สิ่งนี้:
type ArgType = number | string
type Wrapper<T extends ArgType> = {
fn: (a: T) => void
arg: T
}
let foo!: Wrapper<number>
foo.fn(foo.arg)
let foo1!: Wrapper<string>
foo1.fn(foo1.arg)
