How to implement Typescript Function overload
Tags: typescript, frontend
Published: 2023-10-16 15:00:00

👋 Hey folks! In this post, I want to cover the topic of function overloading and explain why it's essential to understand this concept, and why you shouldn't be afraid to use it!

## 📃 Wiki Definition

> Function overloading or method overloading is the ability to create multiple functions of the same name with different implementations. Calls to an overloaded function will run a specific version of that function appropriate to the context of the call.

## 🧐 Wiki Definition Explanation
In short _function overloading_ is a function with multiple call signatures. This means there are multiple ways to call a function.

Example:
Let's take a look at the example of a function which might be called in a few ways with different arguments

```typescript
type ProductParams = {
    promo?: boolean
    isArchived?: boolean;
};

function getProducts(params: ProductParams) {
    const url = new URL('/api/v1/products');
    const urlParams = new URLSearchParams();

    if ('promo' in params) {
        urlParams.append('promo', params.promo.toString());
    }

    if ('isArchived' in params) {
        urlParams.append('isArchived', params.isArchived.toString());
    }

    url.search = urlParams.toString();

    return fetch(url.toString()).then(res => res.json());
}
```

You can call this `function` in 2 different ways, with 2 different params. Our function will return from the `API` products that might be `promo` or `archived`, or both `promo` and `archived`.

## 🔍 Multiple Function Signatures

Typescript's approach to function overloading is quite distinct from some other languages. Instead of having multiple function definitions, TypeScript uses multiple function signatures followed by a single function body.

The type-checker evaluates these signatures from top to bottom. This order matters because TypeScript will use the first signature that matches the function call. For instance, if a function call can match two overloaded signatures, TypeScript will choose the one that's listed first. Therefore, it's advisable to list more specific signatures higher and keep broader ones below.


## 💻 Let's implement the overloading for the example function

```typescript
// Product Interfaces
interface PromoProducts {
    //... some properties specific to promo products
}

interface ArchivedProducts {
    //... some properties specific to archived products
}

interface PromoAndArchivedProducts {
    //... some properties combining both promo and archived products
}

type ProductParams = {
    promo?: boolean
    isArchived?: boolean;
};

// Function overloading signatures
function getProducts(params: { promo: true }): Promise<PromoProducts>;
function getProducts(params: { isArchived: true }): Promise<ArchivedProducts>;
function getProducts(params: { promo: true, isArchived: true }): Promise<PromoAndArchivedProducts>;

function getProducts(params: ProductParams) {
    const url = new URL('/api/v1/products');
    const urlParams = new URLSearchParams();

    if ('promo' in params) {
        urlParams.append('promo', params.maxPrice.toString());
    }

    if ('isArchived' in params) {
        urlParams.append('isArchived', params.isArchived.toString());
    }

    url.search = urlParams.toString();

    return fetch(url.toString()).then(res => res.json());
}
```

After the implementation, we will be able to use our `function` in different ways and have a type of safety in place.

## 🏁 Finish
I recommend not being afraid to use function overloading. It's so helpful when you're providing type-rich interfaces and aiming for better code readability and safety.