簡體   English   中英

有沒有辦法在 TypeScript 中進行方法重載?

[英]Is there a way to do method overloading in TypeScript?

有沒有辦法在 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 中重載 hack 的那部分):

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);

根據規范,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);
    }
}

為清楚起見更新。 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 {
  //...
}

我希望。 我也想要這個功能,但 TypeScript 需要與沒有重載方法的無類型 JavaScript 互操作。 即如果您的重載方法是從 JavaScript 調用的,那么它只能被分派到一個方法實現。

有一些關於 codeplex 的相關討論。 例如

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

我仍然認為 TypeScript 應該生成所有的 if'ing 和切換,所以我們不需要這樣做。

為什么不使用可選屬性定義的接口作為函數參數..

對於這個問題中的情況,使用定義了一些可選屬性的內聯接口只能直接制作如下代碼:

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 中函數重載的使用不像支持真正重載功能的語言那樣舒服。

然而,打字稿中仍然提供了許多新的和方便的東西,這些東西在遺留編程語言中是不可用的,我認為匿名接口中的可選屬性支持是一種滿足遺留函數重載舒適區的方法。

如果方法重載有很多變體,另一種方法是創建一個包含所有參數的類。 所以你可以按任何順序只傳遞你想要的參數。

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);

有沒有辦法用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中重載hack的那一部分):

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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM