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

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 ไปยังโค้ด