TypeScript — function overloading

Rachita Bansal
Dev Genius
Published in
3 min readJul 12, 2020

--

Photo by Glenn Carstens-Peters on Unsplash

One would expect TypeScript to provide function overloading for JS just like any other compiled language e.g. Java and it does, but, somewhat differently.

It’s different from any of the conventional compiled language in that it doesn’t support multiple definitions/implementations of the function with the same function name. TS will throw a compile time error if it finds more than one function implementation with the same name as shown👇

TS code:// ❌ TypeError: Duplicate function implementation.
function isEmpty<T>(a1: T[], a3: T): boolean { ... };
// ❌ TypeError: Duplicate function implementation.
function isEmpty <T>(a1: T, a2: T): boolean { ... };
// ❌ TypeError: Duplicate function implementation.
function isEmpty<T>(a1: AnyLike<T>, a2: AnyLike<T>): boolean {...};
Compiled JS code:function isEmpty(a1, a3) { return false; };function isEmpty(a1, a2) { return false; };function isEmpty(a1, a2) { return false; };

When TS compiles the code to JS, it ends up with 3 identical functions since JS strips off the types. TS prevents this from happening.

However, TS provides function overloading by type checking of the arguments and is achieved in code in the following way:

  • Functions should have the same name with equal number of arguments.
  • A function can have multiple function declarations but only a single implementation of that function.
  • The function signature of the implementation must be a superset/union types of its function declarations.

Note: Functions with same name and different number of arguments are not understood by TS as overloaded functions.

Let’s look at an example.

We define a type called AnyLike which is a union of a generic type T and T[] . T could be a complex object or a primitive here.

// Union type of generic type T and Array of T.
type AnyLike<T> = T[] | T;

Now, consider a function called isEmpty that takes in two argument and returns a boolean. The args can be of type T or T[] as show by lines #1 and #2 respectively. The 3rd one is the actual implementation of the overloaded function which takes in args of type AnyLike<T> — and it needs to be a union type of the T and T[].

//1
function isEmpty <T>(a1: T[], a2: T[]): boolean;
//2
function isEmpty <T>(a1: T, a2: T): boolean;
// function implementation
function isEmpty<T>(a1: AnyLike<T>, a2: AnyLike<T>): boolean {
// logic here
return false;
}
console.log(isEmpty<string>("foo", "bar")); // ✅ compiled!console.log(isEmpty<string>(["foo"], ["bar"])); // ✅ compiled!console.log(isEmpty<{}>({a: "foo"}, {b: "bar"})); // ✅ compiled!console.log(isEmpty<string>("foo", ["bar"])); // ❌ Oops!console.log(isEmpty<string>(["foo"], "bar")); // ❌ Oops!console.log(isEmpty<string>(["foo"], "bar")); // ❌ Oops!

Let’s take a look at another example of function overloading — in a class. We use the same example type AnyLike that we created above here as well,

// overloaded function isEmpty
interface IEmptyType<T> {
isEmpty(a1: T[], a2: T[]): boolean;
isEmpty(a1: T, a2: T): boolean; isEmpty(a1: AnyLike<T>, a2: AnyLike<T>):boolean;
}
class Empty<T> implements IEmptyType<T> {
//Function implementation
public isEmpty(a1: AnyLike<T>, a2: AnyLike<T>): boolean
return false;
}
}
const instance = new Empty<String>();console.log(instance.isEmpty(["hi"], ["bye"])); ✅ compiled!
console.log(instance.isEmpty(["a"], [1,2])); ❌ Oops! // TypeError

Takeaways

Function overloading can be achieved in TS as follows:

  • Functions of the same name with equal number of arguments.
  • Function arguments can have different types and different return type.
  • The argument types and return type of the actual implementation of the function must be a superset or union type of its function declarations.
  • Method overloading can be implemented alike i.e. in a class or outside a class with just functions.

--

--

Software Engineer @ Microsoft | Full-Stack | Data viz| Node.js | React | GraphQL | UI/UX