typescript - พารามิเตอร์แบบ Rest เมื่อโอเวอร์โหลดกับพารามิเตอร์ที่เป็นทางเลือก
เมื่อทำการโอเวอร์โหลดฟังก์ชัน บางครั้งฉันจะใช้รูปแบบดังต่อไปนี้:
export function foo(a: string): void;
export function foo(b: number): void;
export function foo(a: string, b: number): void;
export function foo(...args: [string] | [number] | [string, number]): void {
/* ... */
}
สิ่งนี้ทำให้สามารถกำหนดได้ง่ายว่าโปรโตไทป์ใดถูกใช้ เช่น ถ้า args.length === 2 แสดงว่า args เป็นที่รู้จักว่าเป็น [string, number] สำหรับประเภทที่ซับซ้อนมากขึ้น (ลองพิจารณาว่าถ้าแทนที่จะเป็น string และ number คุณมีสองอินเทอร์เฟซ) สิ่งนี้สามารถทำให้เรื่องซับซ้อนง่ายขึ้นมาก.
อย่างไรก็ตาม ฉันไม่สามารถทำให้สิ่งนี้ทำงานได้เมื่อใช้พารามิเตอร์ที่ไม่บังคับ:
export function foo(a?: string): void;
// ~~~ error TS2394: This overload signature is not compatible with its implementation signature.
export function foo(...args: [] | [string]): void {
return;
}
ฉันรู้ว่าฉันสามารถเพิ่มการโอเวอร์โหลดอีกตัวโดยไม่ใช้พารามิเตอร์ทางเลือกได้ แต่การรวมกันจะเร็ว ๆ นี้กลายเป็นสิ่งที่ควบคุมไม่ได้
export function foo(): void;
export function foo(a: string): void;
export function foo(...args: [] | [string]): void {
return;
}
ตัวอย่างที่ชัดเจนกว่านี้ของเรื่องนี้คือ:
interface Foo {
/* ... */
}
interface Bar {
/* ... */
}
interface Baz {
/* ... */
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args:
| [Foo | undefined, Bar | undefined]
| [Baz, Foo | undefined, Bar | undefined]
): void {
/* ... */
}
เนื่องจากฉันรู้ว่า ถ้า args[0] เป็น Baz ฉันก็จะรู้ส่วนที่เหลือขององค์ประกอบใน args ด้วย อย่างไรก็ตาม มันไม่ทำงานตามที่เห็นด้านบนกับฟังก์ชัน foo
การพิมพ์ทุกการจัดเรียงออกมาจะกลายเป็นตัวแปรหลายแบบ และการพิมพ์แต่ละพารามิเตอร์เป็น arg1?: Foo | Baz, arg2: Foo | Bar, arg3: Bar หมายความว่าฉันต้องตรวจสอบการพิมพ์สำหรับตัวแปรที่เป็นไปไม่ได้
มีวิธีใดที่จะหลีกเลี่ยงปัญหานี้ได้หรือไม่?
คำตอบ #1
โดยพื้นฐานแล้วมีความเทียบเท่าโดยตรงระหว่างรายการพารามิเตอร์ของฟังก์ชันและประเภททัปเปิล โดยเฉพาะพารามิเตอร์ฟังก์ชันที่เป็นทางเลือก เช่น ใน (x?: string) => void จะสอดคล้องกับองค์ประกอบทัปเปิลที่เป็นทางเลือก เช่น [string?] (หรือเมื่อคุณใช้ป้ายกำกับขององค์ประกอบทัปเปิล, [x?: string])
นั่นหมายความว่าคุณสามารถเขียนการใช้งาน overload ของคุณได้ดังนี้:
function foo(a?: string): void;
function foo(...args: [string?]): void {
}
function awesomeFunction(foo?: Foo, bar?: Bar): void;
function awesomeFunction(baz: Baz, foo?: Foo, bar?: Bar): void;
function awesomeFunction(...args: [Foo?, Bar?] | [Baz, Foo?, Bar?]): void {
}
จริงๆ ถ้า ประเภทการคืนค่าตรงกันสำหรับลายเซ็นการเรียกทั้งหมด คุณก็ไม่จำเป็นต้องใช้การโอเวอร์โหลดเลย:
function bar(...args: [a?: string]): void { }
// bar(a?: string | undefined): void
และฟังก์ชันที่รับยูเนียนของทูเพิลส่วนที่เหลือจะดูเหมือนฟังก์ชันที่มีการโอเวอร์โหลดเมื่อคุณเรียกใช้มัน:
function awesomeFunction(...args:
[foo?: Foo, bar?: Bar] | [baz: Baz, foo?: Foo, bar?: Bar]
): void { }
// 1/2 awesomeFunction(foo?: Foo | undefined, bar?: Bar | undefined): void
// 2/2 awesomeFunction(baz: Baz, foo?: Foo | undefined, bar?: Bar | undefined): void
และถึงแม้ว่าจะกล่าวได้ว่ามีความสัมพันธ์ระหว่าง [string?] และ [string] | [] แต่ก็อาจจะไม่มีประโยชน์ที่จะพยายามบังคับให้คอมไพเลอร์เห็นมันเมื่อมีองค์ประกอบทูเพิลที่เป็นทางเลือกอยู่
ลิงก์ Playground ไปยังโค้ด
