Is there a way to do method overloading in TypeScript?

JavascriptTypescriptOperator Overloading

Javascript Problem Overview


Is there a way to do method overloading in TypeScript language?

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

Javascript Solutions


Solution 1 - Javascript

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

Solution 2 - Javascript

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

Solution 3 - Javascript

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.

Solution 4 - Javascript

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.

Solution 5 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVoid-995View Question on Stackoverflow
Solution 1 - JavascriptsvickView Answer on Stackoverflow
Solution 2 - JavascriptFentonView Answer on Stackoverflow
Solution 3 - JavascriptnicopolypticView Answer on Stackoverflow
Solution 4 - Javascript千木郷View Answer on Stackoverflow
Solution 5 - JavascriptIgorView Answer on Stackoverflow