如何在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);

如何在TypeScript语言中进行方法重载?


答案 1

根据规范,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);
    }
}

答案 2

为清楚起见,请进行更新。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 {

始终是必需的,因此它首先进行。你可以把它写成一个工作的TypeScript重载:stringParameter

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 {
  //...
}