Function Overloading in Typescript
Tags: typescript, angular
Published: 2023-03-27 08:18:00

Typescript is a type system built on top of Javascript and its only purpose is to secure your application by applying strong type security.

Thanks to Typescript, you can reduce runtime errors by catching them at build time. As a good teammate, it's helpful to create strong-typed functions when creating utilities functions. By doing so, auto-completion and type inference will assist your teammates when using your function, and it will reduce errors.

Let's see how we can achieve that by looking at a technique called **function overloading**.

---

In this example, we aim to create a function with the following capabilities:
- Filter an array of strings by checking if each element starts with a specified search parameter. 
- Choose to return either the first match or all elements that matched. 
- Check if a given string starts with the search parameter

The function below demonstrates how to implement these conditions:

```typescript
function select(arr: string[] | string, search: string, firstOnly = false){
        // ^? string | boolean | string[] | undefined
  if(Array.isArray(arr)){
    return firstOnly 
            ? arr.find(a => a.startsWith(search))
            : arr.filter(a => a.startsWith(search))
  }else {
    return arr.startsWith(search);
  }
}
```
**Note:** The implementation details are not the focus of this article.

Now, let's call this fonction to return all elements that match the search parameter:

```typescript
const names = ['toto', 'jack', 'robert'];
const t = filter(names, 'to');
//    ^? string | boolean | string[] | undefined
```
**Issue:** The inferred return type is `string | boolean | string[] | undefined`. Typescript is not able to narrow down the return type to string[], even though we know that's the only possible return type.

![typescript cannot narrow type](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5vt57jf0c6wk8fx4z3w1.png)


In this second example, if we want to use the function for checking if the given string matches the search parameter, Typescript allows us to set the `firstOnly` parameter which is useless and confusing in our case.

---

To address these issues, we can overload our `filter` function. Overloading involves defining multiple versions of the same function with different parameter types and return types.

In this example, we define three different versions of the filter function:

```typescript
function filter(arr:string[], search: string): string[];
function filter(arr:string[], search: string, firstOnly: true): string | undefined;
function filter(elt:string, search: string): boolean;

function filter(arr: string[] | string, search: string, firstOnly = false){
  if(Array.isArray(arr)){
    return firstOnly ? arr.find(a => a.startsWith(search)): arr.filter(a => a.startsWith(search))
  }else {
    return arr.startsWith(search);
  }
}
```
The first version takes an array of strings and a search parameter and returns an array of matching strings. The second version adds a `firstOnly` parameter which, when set to `true`, returns only the first matching string or undefined if no match is found. The third version takes a single string and a search parameter and returns a boolean value indicating whether the string starts with the search parameter.

The implementation of the `filter` function then concatenates all the definitions into one. However, this combined definition cannot be used directly in the code. If we want to call the `filter` function with a string as the first parameter and use the boolean as the last parameter, we need to add a new function definition.

Overloading the `filter` function has several benefits:
- The `firstOnly` parameter can only be set when the first parameter is an array.
- The inferred type is correctly narrowed depending on the function's parameters.

Examples:

![error thanks to auto-completion](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/95bf2vg1osnqp59q516n.png)


The image shows an error thrown by Typescript because we have set the `firstOnly` parameter but our first argument is a `string` and no function definition matches this implementation.

```typescript
const names = ['toto', 'jack', 'robert'];
const first = filter(names, 'to', true);
//    ^? string | undefined

const second = filter(names, 'to');
//    ^? string[]

const third = filter('toto', 'to')
//    ^? boolean
```
The function calls get a nice auto-completion and type inference making working with the `filter` function easier.

**Warning:** However there is still a significant warning to consider.

While calling the function has become much safer, we have lost all type safety inside the implementation. By telling TypeScript that we know the types better than it does, TypeScript will not protect us if our definitions are incorrect.

Let's illustrate this warning with an example of how we could deceive our team members by lying about the function's behavior:

```typescript
function lying(elt:string): number;
function lying(elt:number): string;

function lying(elt: string | number){
  return elt;
}
```

```typescript
const t = lying('toto');
//    ^? number
```
In the above code, the function is simple, but it demonstrates the problem perfectly. We have set two definitions that TypeScript will trust, but as we can see, the return type is incorrect. When we call the function, the inferred type of t is incorrect.

> _While function overloading can be incredibly helpful, it's essential to be cautious and ensure that the types are as accurate as possible. Sometimes, it's better to have less accurate types than to lie to our users._

---

I hope I have helped you learn about function overloading in Typescript! It's definitely an advanced feature, but it can be very useful in making your code safer and more reliable. Remember to be cautious when using function overloading, as it can lead to type safety issues if not used carefully.

Good luck with your Typescript projects, and don't hesitate to reach out to me if you have any more questions! You can find me on [Twitter](https://twitter.com/laforge_toma) or [Github](https://github.com/tomalaforge).