Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to do method overloading in TypeScript?

People also ask

What is overloading in TypeScript?

In TypeScript, function overloading, or method overloading, is the ability to create multiple methods with the same name and same return type, but a different number of parameters or different parameter types. So essentially, method overloading is allowed when – Function name is same.

Can a class overload properties in TypeScript?

Method overloading in Typescript differs from traditional programming languages like Java or C#. To overload methods, you can either choose optional parameters or function declarations.

Is method overloading possible in JavaScript?

JavaScript does not support function overloading natively. If we will add functions with the same name and different arguments, it considers the last defined function.

Is method overloading is possible?

Yes, by method overloading. You can have any number of main methods in a class by method overloading. But JVM calls main() method which receives string array as arguments only.


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

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

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.


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.