มีวิธีทำ method overloading ใน TypeScript หรือไม่?


111

มีวิธีทำ method overloading ในภาษา TypeScript หรือไม่?

ฉันต้องการบรรลุสิ่งนี้:

class TestClass {
    someMethod(stringParameter: string): void {
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    someMethod(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }
}

var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");

นี่คือตัวอย่างของสิ่งที่ฉันไม่ต้องการทำ (ฉันเกลียดส่วนนั้นของการแฮ็กใน JS มากเกินไป):

class TestClass {
    private someMethod_Overload_string(stringParameter: string): void {
        // A lot of code could be here... I don't want to mix it with switch or if statement in general function
        alert("Variant #1: stringParameter = " + stringParameter);
    }

    private someMethod_Overload_number_string(numberParameter: number, stringParameter: string): void {
        alert("Variant #2: numberParameter = " + numberParameter + ", stringParameter = " + stringParameter);
    }

    private someMethod_Overload_string_number(stringParameter: string, numberParameter: number): void {
        alert("Variant #3: stringParameter = " + stringParameter + ", numberParameter = " + numberParameter);
    }

    public someMethod(stringParameter: string): void;
    public someMethod(numberParameter: number, stringParameter: string): void;
    public someMethod(stringParameter: string, numberParameter: number): void;

    public someMethod(): void {
        switch (arguments.length) {
        case 1:
            if(typeof arguments[0] == "string") {
                this.someMethod_Overload_string(arguments[0]);
                return;
            }
            return; // Unreachable area for this case, unnecessary return statement
        case 2:
            if ((typeof arguments[0] == "number") &&
                (typeof arguments[1] == "string")) {
                this.someMethod_Overload_number_string(arguments[0], arguments[1]);
            }
            else if ((typeof arguments[0] == "string") &&
                     (typeof arguments[1] == "number")) {
                this.someMethod_Overload_string_number(arguments[0], arguments[1]);
            }
            return; // Unreachable area for this case, unnecessary return statement
        }
    }
}


var testClass = new TestClass();
testClass.someMethod("string for v#1");
testClass.someMethod(12345, "string for v#2");
testClass.someMethod("string for v#3", 54321);

1
ใช่มีโอกาสภาษายังไม่ตาย มีคำถามเพิ่มเติมหรือไม่?
hakre

6
@hakre เป็นเรื่องแปลกที่ต้องพูดเนื่องจาก TypeScript รองรับวิธีการโอเวอร์โหลดแล้ว
svick

@svick: คุณเรียกวิธีนั้นว่าโอเวอร์โหลดหรือไม่? ในคำตอบของคุณวิธีการนั้นไม่ได้ใช้งานมากเกินไปมีร่างกายเดียวหากอยู่รอบ ๆ
hakre

2
@hakre ข้อกำหนดนี้เรียกว่าวิธีการโอเวอร์โหลด คุณสามารถโต้แย้งได้อย่างแน่นอนว่ามันไม่ใช่เวอร์ชันที่ดีโดยเฉพาะ แต่ฉันคิดว่าคุณไม่สามารถพูดได้ว่าไม่มีอยู่จริง
svick

@svick: ฉันไม่ได้พูดเหมือนกัน แต่สำหรับฉันแล้วดูเหมือนว่าโอกาสที่ OP ถามถึงนั้นมีความเฉพาะเจาะจงเกี่ยวกับรูปแบบทางจิตของการโอเวอร์โหลด สำหรับการแยกผมเราสามารถพูดได้ว่ามันเป็นวิธีการที่ลายเซ็นมากเกินไป)
hakre

คำตอบ:


171

ตามข้อกำหนด TypeScript รองรับวิธีการโอเวอร์โหลด แต่ค่อนข้างอึดอัดและมีการตรวจสอบประเภทพารามิเตอร์ด้วยตนเองจำนวนมาก ฉันคิดว่าส่วนใหญ่เป็นเพราะสิ่งที่ใกล้เคียงที่สุดที่คุณสามารถเข้าถึงวิธีการโอเวอร์โหลดใน JavaScript ธรรมดารวมถึงการตรวจสอบด้วยและ TypeScript พยายามที่จะไม่แก้ไขเนื้อความของวิธีการจริงเพื่อหลีกเลี่ยงต้นทุนประสิทธิภาพรันไทม์ที่ไม่จำเป็น

ถ้าฉันเข้าใจถูกต้องก่อนอื่นคุณต้องเขียนการประกาศวิธีการสำหรับการโอเวอร์โหลดแต่ละครั้ง ใช้วิธีการหนึ่งที่ตรวจสอบอาร์กิวเมนต์เพื่อตัดสินใจว่าโอเวอร์โหลดใดถูกเรียกใช้ ลายเซ็นของการใช้งานจะต้องเข้ากันได้กับโอเวอร์โหลดทั้งหมด

class TestClass {
    someMethod(stringParameter: string): void;
    someMethod(numberParameter: number, stringParameter: string): void;

    someMethod(stringOrNumberParameter: any, stringParameter?: string): void {
        if (stringOrNumberParameter && typeof stringOrNumberParameter == "number")
            alert("Variant #2: numberParameter = " + stringOrNumberParameter + ", stringParameter = " + stringParameter);
        else
            alert("Variant #1: stringParameter = " + stringOrNumberParameter);
    }
}

3
@NicoVanBelle JavaScript ไม่สนับสนุนวิธีการโอเวอร์โหลดเลยใช่ไหม แล้ว JS จะกลับไปช่วยยังไง?
svick

สิ่งที่เกี่ยวกับการตรวจสอบอินเทอร์เฟซ? คุณมีทางออกที่ดีกว่านั้น: stackoverflow.com/questions/14425568/… ?
DiPix

อืม .. ฉันไม่ชอบแบบนี้จริงๆแค่มีพารามิเตอร์ที่เป็นทางเลือกแทนก็น่าอ่านกว่า
LuckyLikey

3
ฉันคิดว่าสิ่งที่สำคัญที่นี่คือการทำให้รหัสของคุณอ่านได้โดยไม่ต้องตรวจสอบข้อความจำนวนมาก ใครจะสนใจว่ามันจะเกิดอะไรขึ้น
Brain2000

35

อัปเดตเพื่อความชัดเจน การโอเวอร์โหลดเมธอดใน TypeScript เป็นคุณสมบัติที่มีประโยชน์ตราบเท่าที่ช่วยให้คุณสร้างนิยามประเภทสำหรับไลบรารีที่มีอยู่ด้วย API ที่จำเป็นต้องแสดง

อย่างไรก็ตามเมื่อเขียนโค้ดของคุณเองคุณอาจสามารถหลีกเลี่ยงโอเวอร์เฮดด้านความรู้ความเข้าใจของการโอเวอร์โหลดโดยใช้พารามิเตอร์ทางเลือกหรือพารามิเตอร์เริ่มต้น นี่เป็นทางเลือกที่อ่านง่ายกว่าสำหรับวิธีการโอเวอร์โหลดและยังช่วยให้ API ของคุณซื่อสัตย์เนื่องจากคุณจะหลีกเลี่ยงการสร้างโอเวอร์โหลดด้วยการสั่งซื้อที่ไม่ได้ตั้งใจ

กฎหมายทั่วไปของการโอเวอร์โหลด TypeScript คือ:

หากคุณสามารถลบลายเซ็นโอเวอร์โหลดและการทดสอบทั้งหมดของคุณผ่านคุณไม่จำเป็นต้องโอเวอร์โหลด TypeScript

โดยปกติคุณสามารถบรรลุสิ่งเดียวกันได้โดยใช้พารามิเตอร์ที่เป็นทางเลือกหรือพารามิเตอร์เริ่มต้นหรือด้วยประเภทการรวมกันหรือด้วยการวางแนววัตถุเล็กน้อย

คำถามจริง

คำถามที่เกิดขึ้นจริงถามว่า:

someMethod(stringParameter: string): void {

someMethod(numberParameter: number, stringParameter: string): void {

ขณะนี้แม้แต่ในภาษาที่รองรับการโอเวอร์โหลดด้วยการใช้งานแยกกัน (หมายเหตุ: การโอเวอร์โหลด TypeScript จะแชร์การใช้งานครั้งเดียว) - โปรแกรมเมอร์เป็นคำแนะนำเพื่อให้มีความสม่ำเสมอในการสั่งซื้อ สิ่งนี้จะทำให้ลายเซ็น:

someMethod(stringParameter: string): void {

someMethod(stringParameter: string, numberParameter: number): void {

stringParameterถูกต้องเสมอดังนั้นมันไปก่อน คุณสามารถเขียนสิ่งนี้เป็นโอเวอร์โหลด TypeScript ที่ใช้งานได้:

someMethod(stringParameter: string): void;
someMethod(stringParameter: string, numberParameter: number): void;
someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

แต่ตามกฎหมายของการโอเวอร์โหลด TypeScript เราสามารถลบลายเซ็นโอเวอร์โหลดและการทดสอบทั้งหมดของเราจะยังคงผ่าน

someMethod(stringParameter: string, numberParameter?: number): void {
    if (numberParameter != null) {
        // The number parameter is present...
    }
}

คำถามจริงตามลำดับจริง

หากคุณตั้งใจแน่วแน่ที่จะปฏิบัติตามคำสั่งเดิมการโอเวอร์โหลดจะเป็น:

someMethod(stringParameter: string): void;
someMethod(numberParameter: number, stringParameter: string): void;
someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

ตอนนี้มีการแตกแขนงมากมายเพื่อหาว่าจะวางพารามิเตอร์ไว้ที่ใด แต่คุณต้องการรักษาคำสั่งนี้ไว้หากคุณอ่านจนถึงตอนนี้ แต่เดี๋ยวก่อนจะเกิดอะไรขึ้นถ้าเราใช้กฎของ TypeScript มากเกินไป

someMethod(a: string | number, b?: string | number): void {
  let stringParameter: string;
  let numberParameter: number;

  if (typeof a === 'string') {
    stringParameter = a;
  } else {
    numberParameter = a;

    if (typeof b === 'string') {
      stringParameter = b;
    }
  }
}

พอแตกกิ่งก้านแล้ว

แน่นอนเมื่อพิจารณาถึงจำนวนการตรวจสอบประเภทที่เราต้องทำ ... บางทีคำตอบที่ดีที่สุดคือมีสองวิธี:

someMethod(stringParameter: string): void {
  this.someOtherMethod(0, stringParameter);
}

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}

ซึ่งโดยทั่วไปไม่เรียกว่าวิธีการโอเวอร์โหลด นอกจากนี้ยังเห็นคำถามประเภทของพารามิเตอร์แรกเปลี่ยนไป
hakre

3
ฉันยอมรับว่าในคำตอบของฉัน - คุณต้องใส่พารามิเตอร์ที่เป็นทางเลือกสุดท้ายดังนั้นnumberพารามิเตอร์จะเป็นอาร์กิวเมนต์ที่สองและจะเป็นทางเลือกก็ได้ TypeScript ไม่สนับสนุนการโอเวอร์โหลดเมธอด "ที่เหมาะสม" - แต่ถึงแม้โลก C # จะเคลื่อนออกจากการโอเวอร์โหลดไปยังพารามิเตอร์ที่เป็นทางเลือกเนื่องจากในหลาย ๆ กรณีจะทำให้เกิดโค้ดที่อ่านได้มากขึ้น
Fenton

คุณหมายถึงif (typeof numberParameter != 'undefined')ใช่;)
Juan Mendes

ขึ้นอยู่กับกรณีการใช้งานของคุณโดยเฉพาะว่าคุณยอมรับศูนย์หรือไม่ หากคุณจำเป็นต้องทำอย่าลืมใช้!==เพื่อหลีกเลี่ยงการเล่นกล
Fenton

1
@ เซบาสเตียนที่ดูเหมือนเป็นโอกาสสำหรับการฉีดพึ่งพา เนื่องจากวิธีการโอเวอร์โหลดใน TypeScript เกี่ยวข้องกับวิธีการเดียวที่มีการตกแต่งหลายพารามิเตอร์เริ่มต้นและประเภทการรวมกันทำให้ได้รับประสบการณ์ที่ดีขึ้น หากวิธีการแตกต่างกันไปในหลาย ๆ วิธีคุณอาจใช้นามธรรมหรือใช้หลายวิธีก็ได้
Fenton

7

ฉันหวังว่า. ฉันต้องการคุณสมบัตินี้ด้วย แต่ TypeScript ต้องทำงานร่วมกันได้กับ JavaScript ที่ไม่ได้พิมพ์ซึ่งไม่มีเมธอดมากเกินไป กล่าวคือถ้าเมธอดของคุณถูกเรียกใช้จาก JavaScript มันจะถูกส่งไปยังการใช้งานวิธีเดียวเท่านั้น

มีการสนทนาที่เกี่ยวข้องเล็กน้อยเกี่ยวกับ codeplex เช่น

https://typescript.codeplex.com/workitem/617

ฉันยังคิดว่า TypeScript ควรสร้าง if'ing และ switching ทั้งหมดดังนั้นเราจึงไม่จำเป็นต้องทำ


2

เหตุใดจึงไม่ใช้อินเตอร์เฟสที่กำหนดคุณสมบัติทางเลือกเป็นอาร์กิวเมนต์ของฟังก์ชัน ..

สำหรับกรณีในคำถามนี้การใช้อินเทอร์เฟซแบบอินไลน์ที่กำหนดด้วยคุณสมบัติเสริมบางอย่างเท่านั้นที่สามารถสร้างโค้ดได้โดยตรงเช่นด้านล่าง:

class TestClass {

    someMethod(arg: { stringParameter: string, numberParameter?: number }): void {
        let numberParameterMsg = "Variant #1:";
        if (arg.numberParameter) {
            numberParameterMsg = `Variant #2: numberParameter = ${arg.numberParameter},`;
        }
        alert(`${numberParameterMsg} stringParameter = ${arg.stringParameter}`);
    }
}

var testClass = new TestClass();
testClass.someMethod({ stringParameter: "string for v#1" });
testClass.someMethod({ numberParameter: 12345, stringParameter: "string for v#2" });

เนื่องจากการโอเวอร์โหลดที่ระบุไว้ใน TypeScript นั้นเป็นไปตามที่กล่าวไว้ในความคิดเห็นของผู้อื่นเพียงแค่รายการลายเซ็นที่แตกต่างกันของฟังก์ชันโดยไม่สนับสนุนโค้ดการใช้งานที่สอดคล้องกันเช่นภาษาคงที่อื่น ๆ ดังนั้นการนำไปใช้งานยังคงต้องทำในเนื้อความฟังก์ชันเดียวซึ่งทำให้การใช้งานฟังก์ชันมากเกินไปใน typescript ไม่สะดวกสบายเท่าที่ภาษาดังกล่าวรองรับคุณสมบัติการโอเวอร์โหลดจริง

อย่างไรก็ตามยังมีสิ่งใหม่ ๆ และสะดวกสบายมากมายที่มีให้ใน typescript ซึ่งไม่มีในภาษาการเขียนโปรแกรมแบบเดิมซึ่งการสนับสนุนคุณสมบัติเสริมในอินเทอร์เฟซที่ไม่ระบุตัวตนเป็นวิธีการดังกล่าวเพื่อตอบสนองโซนที่สะดวกสบายจากการทำงานแบบเดิมมากเกินไป


0

หากมีการโอเวอร์โหลดเมธอดหลายรูปแบบอีกวิธีหนึ่งคือสร้างคลาสที่มี args ทั้งหมดของคุณอยู่ภายใน ดังนั้นคุณสามารถส่งเฉพาะพารามิเตอร์ที่คุณต้องการในลำดับใดก็ได้

class SomeMethodConfig {
  stringParameter: string;
  numberParameter: number;

 /**
 *
 */
 constructor(stringParameter: string = '012', numberParameter?: number) { // different ways to make a param optional
   this.numberParameter = 456; // you can put a default value here
   this.stringParameter = stringParameter; // to pass a value throw the constructor if necessary
 }
}

นอกจากนี้คุณสามารถสร้างตัวสร้างที่มีค่าเริ่มต้นและ / หรือส่งผ่านอาร์กิวเมนต์ที่จำเป็น จากนั้นใช้ดังนี้:

const config = new SomeMethodConfig('text');
config.numberParameter = 123; // initialize an optional parameter only if you want to do it
this.SomeMethod(config);

-2

Javascript ไม่มีแนวคิดเรื่องการโอเวอร์โหลด typescript ไม่ใช่ c # หรือ Java

แต่คุณสามารถใช้การโอเวอร์โหลดได้ใน typescript

อ่านโพสต์นี้ http://www.gyanparkash.in/function-overloading-in-typescript/


น่าเสียดายที่ลิงค์เสีย คุณสามารถโพสต์ใหม่ได้หรือไม่?
LHM

@LMH: นั่นเป็นเหตุผลที่ไม่สนับสนุนคำตอบแบบลิงก์เท่านั้น
Dan Dascalescu
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.