有没有办法在TypeScript中进行方法重载?


109

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

1
是的,有机会,语言还没死。还有其他问题吗?
hakre 2012年

6
@hakre说起来很奇怪,考虑到TypeScript已经支持方法重载。
svick

@svick:好吧,您称该方法重载吗?在您的答案中,方法本身不会过载,如果周围有一个物体。
hakre 2012年

2
@hakre规范确实将其称为方法重载。您当然可以说它不是一个特别好的版本,但是我想您不能说它根本不存在。
svick 2012年

@svick:我也没说。但是在我看来,OP询问的机会是关于方法重载的心理模型的特定机会。对于头发分裂,我们可以说这是方法签名重载;)
hakre 2012年

Answers:


165

根据规范,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
我认为重要的是,它可以使您的代码保持可读性,而无需进行大量的if语句类型检查。谁在乎它转换成什么。
Brain2000 '19

34

为清楚起见进行更新。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 2012年

3
我承认,在我的回答中-您会将可选参数放在最后,因此该number参数将是第二个参数,并且是可选的。TypeScript不支持“适当的”方法重载-但是,即使在C#世界中,它也从重载向可选参数转移,因为在许多情况下,这会导致代码更具可读性。
Fenton

你是说if (typeof numberParameter != 'undefined')对的;)
Juan Mendes

这取决于您的用例,特别是您是否接受零。如果需要这样做,请记住使用!==以避免杂耍。
芬顿

1
@Sebastian听起来像是依赖注入的机会。鉴于TypeScript中的方法重载涉及具有多个修饰符,默认参数和联合类型的单个方法,因此提供了更好的体验。如果方法在本质上有所不同,则可以使用抽象或实现多个方法。
Fenton

7

我希望。我也想要此功能,但TypeScript需要与没有重载方法的无类型JavaScript互操作。即,如果您的重载方法是从JavaScript调用的,则只能将其分派到一个方法实现中。

关于Codeplex的相关讨论很少。例如

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

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


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中函数重载的使用不像支持真正重载功能的语言那样舒适。

但是,仍然有很多以打字稿提供的方便的新东西,而这在旧版编程语言中是不可用的,我认为,匿名接口中的可选属性支持就是这样一种方法,可以满足旧版函数重载的要求。


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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.