143

I want to achieve something like this:

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

Here is an example of what I don't want to do (I really hate that part of overloading hack in 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);

How to do method overloading in TypeScript language?

Vega
  • 27,856
  • 27
  • 95
  • 103
Void-995
  • 1,735
  • 2
  • 13
  • 8
  • 6
    @hakre That's a weird thing to say, considering TypeScript already does support method overloading. – svick Oct 02 '12 at 11:01
  • 1
    @svick: well, do you call that method overloading? In your answer the method itself is not overloaded, one body if'ing around. – hakre Oct 02 '12 at 11:14
  • 2
    @hakre The specification does call it method overloading. You can certainly argue that it's not a particularly nice version of it, but I think you can't say that it doesn't exist at all. – svick Oct 02 '12 at 11:21
  • @svick: I didn't say either. But it looks to me that the chances OP asks about are specific about the mental model of method overloading. For the hair-splitting we could say it's method signature overloading ;) – hakre Oct 02 '12 at 11:23
  • 1
    Does this answer your question? [TypeScript function overloading](https://stackoverflow.com/questions/13212625/typescript-function-overloading) – Michael Freidgeim May 31 '20 at 01:50

5 Answers5

226

According to the specification, TypeScript does support method overloading, but it's quite awkward and includes a lot of manual work checking types of parameters. I think it's mostly because the closest you can get to method overloading in plain JavaScript includes that checking too and TypeScript tries to not modify actual method bodies to avoid any unnecessary runtime performance cost.

If I understand it correctly, you have to first write a method declaration for each of the overloads and then one method implementation that checks its arguments to decide which overload was called. The signature of the implementation has to be compatible with all of the overloads.

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);
    }
}
svick
  • 236,525
  • 50
  • 385
  • 514
  • 3
    @NicoVanBelle JavaScript doesn't support method overloading at all, right? So how is going back to JS going to help? – svick Sep 11 '17 at 09:26
  • What about checking interfaces? Have you better solution than that: https://stackoverflow.com/questions/14425568/interface-type-check-with-typescript?utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa ? – DiPix Mar 28 '18 at 10:19
  • hmm.. i dont really like this, just having an optional parameter instead is better to read. – LuckyLikey Nov 13 '18 at 15:22
  • 3
    I think what is important here is it keeps your code readable without a ton of typechecking if statements. Who cares what it transpiles into. – Brain2000 Jun 15 '19 at 23:07
45

Update for clarity. Method overloading in TypeScript is a useful feature insofar as it allows you to create type definitions for existing libraries with an API that needs to be represented.

When writing your own code, though, you may well be able to avoid the cognitive overhead of overloads using optional or default parameters. This is the more readable alternative to method overloads and also keeps your API honest as you'll avoid creating overloads with unintuitive ordering.

The general law of TypeScript overloads is:

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

You can usually achieve the same thing with optional, or default parameters - or with union types, or with a bit of object-orientation.

The Actual Question

The actual question asks for an overload of:

someMethod(stringParameter: string): void {

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

Now even in languages that support overloads with separate implementations (note: TypeScript overloads share a single implementation) - programmers are advices to provide consistency in ordering. This would make the signatures:

someMethod(stringParameter: string): void {

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

The stringParameter is always required, so it goes first. You could write this as a working TypeScript overload:

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

But following the law of TypeScript overloads, we can delete the overload signatures and all our tests will still pass.

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

The Actual Question, In the Actual Order

If you were determined to persist with the original order, the overloads would be:

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

Now that's a lot of branching to work out where to put the parameters, but you really wanted to preserve this order if you are reading this far... but wait, what happens if we apply the law of TypeScript overloads?

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

Enough Branching Already

Of course, given the amount of type checking we need to do... maybe the best answer is simply to have two method:

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

someOtherMethod(numberParameter: number, stringParameter: string): void {
  //...
}
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 1
    that is commonly not known as method overloading. also see the question, the type of the first parameter changes. – hakre Oct 02 '12 at 10:49
  • 3
    I acknowledged that in my answer - you would put the optional parameter last, so the `number` parameter would be the second argument and would be optional. TypeScript doesn't support "proper" method overloads - but even the C# world is moving away from overloads towards optional parameters as in many cases it leads to more readable code. – Fenton Oct 02 '12 at 14:02
  • You mean `if (typeof numberParameter != 'undefined')`, right ;) – Ruan Mendes Oct 28 '16 at 05:25
  • That depends on your use case, in particular whether you accept zeroes. If you need to do it, remember to use `!==` to avoid juggling. – Fenton Oct 31 '16 at 12:52
  • this is just a use case. Overloading means... well, overloading and not default arguments. You may need or use a method that its input could be retrieved from files, from an array or from an array of files. You won't go any further with optional and default behaviour, you will only make your code less readable. – Sebastian Dec 06 '17 at 03:56
  • 1
    @Sebastian that sounds like an opportunity for dependency injection. Given that method overloading in TypeScript involves a single method with multiple decorations, default parameters, and union types, provide a better experience. If the methods differ in more substantial ways, you either use an abstraction, or implement multiple methods. – Fenton Dec 06 '17 at 13:34
7

I wish. I want this feature too but TypeScript needs to be interoperable with untyped JavaScript which doesn't have overloaded methods. i.e. If your overloaded method is called from JavaScript then it can only get dispatched to one method implementation.

There\s a few relevant discussions on codeplex. e.g.

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

I still think TypeScript should generate all the if'ing and switching so we wouldn't need to do it.

nicopolyptic
  • 227
  • 3
  • 5
2

Why not to use optional property defined interface as the function argument..

For the case in this question, using an inline interface defined with some optional properties only could directly make code like something below:

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

Because overloading provided in TypeScript is, as mentioned in others' comments, just a list of function's different signatures without supporting corresponding implementation codes like other static languages. So the implementation still have to be done in only one function body, which makes the usage of function overloading in Typescript not as comfortable as such languages supporting the real overloading feature.

However, there is still many new and convenient stuffs provided in typescript which is not available in legacy programming language, where optional property support in an anonymous interface is such an approach to meet the comfortable zone from the legacy function overloading, I think.

千木郷
  • 1,595
  • 2
  • 19
  • 30
0

If there are a lot of variations of method overloads, another way is just to create a class with all your args inside. So you can pass only parameter you want in any order.

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
 }
}

Also you can create a constructor with default values and/or pass some mandatory arguments. Then just use like this:

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