【问题标题】:Is there a way to do method overloading in TypeScript?有没有办法在 TypeScript 中进行方法重载?
【发布时间】:2012-09-23 04:30:10
【问题描述】:

有没有办法在 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);

【问题讨论】:

  • @hakre 说起来有点奇怪,因为 TypeScript 已经支持方法重载了。
  • @svick:嗯,你称该方法为重载吗?在你的回答中,方法本身没有重载,如果有一个身体。
  • @hakre 规范确实将其称为方法重载。你当然可以说它不是一个特别好的版本,但我认为你不能说它根本不存在。
  • @svick:我也没说。但在我看来,OP 询问的机会是特定于方法重载的心理模型的。对于头发分裂,我们可以说它是方法签名重载;)
  • 这能回答你的问题吗? TypeScript function overloading

标签: javascript typescript operator-overloading


【解决方案1】:

为清晰起见更新。 TypeScript 中的方法重载是一项有用的功能,因为它允许您使用需要表示的 API 为现有库创建类型定义。

不过,在编写自己的代码时,您可能能够使用可选参数或默认参数来避免重载的认知开销。这是比方法重载更具可读性的替代方法,并且还可以保持 API 诚实,因为您将避免使用不直观的顺序创建重载。

TypeScript 重载的一般规律是:

If you can delete the overload signatures and all of your tests pass, you don’t need TypeScript overloads

您通常可以使用可选参数或默认参数来实现相同的目的 - 或者使用联合类型,或者使用一点面向对象。

实际问题

实际问题要求重载:

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

【讨论】:

  • 通常不称为方法重载。另见问题,第一个参数的类型发生了变化。
  • 我承认在我的回答中 - 您会将可选参数放在最后,因此 number 参数将是第二个参数并且是可选的。 TypeScript 不支持“正确”的方法重载 - 但即使是 C# 世界也正在从重载转向可选参数,因为在许多情况下它会导致代码更具可读性。
  • 你的意思是if (typeof numberParameter != 'undefined'),对;)
  • 这取决于您的用例,尤其是您是否接受零。如果需要,记得使用!==,避免杂耍。
  • @Sebastian 这听起来像是依赖注入的机会。鉴于 TypeScript 中的方法重载涉及具有多个装饰、默认参数和联合类型的单个方法,因此可以提供更好的体验。如果这些方法在本质上有所不同,您可以使用抽象,或者实现多个方法。
【解决方案2】:

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

【讨论】:

  • @NicoVanBelle JavaScript 根本不支持方法重载,对吧?那么回到 JS 会有什么帮助呢?
  • 检查接口呢?你有比这更好的解决方案吗:stackoverflow.com/questions/14425568/…
  • hmm.. 我不太喜欢这个,只是有一个可选参数更好阅读。
  • 我认为这里重要的是它使您的代码保持可读性,而无需大量的类型检查 if 语句。谁在乎它转译成什么。
【解决方案3】:

我希望。我也想要这个功能,但 TypeScript 需要与没有重载方法的无类型 JavaScript 互操作。也就是说,如果你的重载方法是从 JavaScript 调用的,那么它只能被分派到一个方法实现。

有一些关于 codeplex 的相关讨论。例如

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

我仍然认为 TypeScript 应该生成所有的 if'ing 和 switch,所以我们不需要这样做。

【讨论】:

    【解决方案4】:

    为什么不使用optional property defined interface 作为函数参数..

    对于这个问题的情况,使用定义了一些可选属性的内联接口只能直接编写如下代码:

    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 中提供的重载,正如其他人的 cmets 所提到的,只是一个函数不同签名的列表,而不像其他静态语言那样支持相应的实现代码。所以实现仍然只需要在一个函数体中完成,这使得 Typescript 中函数重载的使用不如支持真正重载特性的语言那么舒服。

    然而,打字稿中仍然提供了许多新的和方便的东西,这些东西在传统编程语言中是不可用的,我认为匿名接口中的可选属性支持是一种满足传统函数重载舒适区的方法.

    【讨论】:

      【解决方案5】:

      如果方法重载有很多变体,另一种方法是创建一个包含所有参数的类。所以你可以按任何顺序只传递你想要的参数。

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

      【讨论】:

        猜你喜欢
        • 2012-01-20
        • 1970-01-01
        • 2022-06-10
        • 2015-09-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多