TypeScript Tip: Using Conditional Types To Refactor Overloads.
Tags: typescript, javascript, webdev
Published: 2022-04-04 21:46:45

---
title: TypeScript Tip: Using Conditional Types To Refactor Overloads.
published: true
description: Conditional types let us express type transformations that differ depending on a condition, let's see how it can be used to refactorize a function with overloads
tags: typescript, javascript, webdev
cover_image: https://javadesde0.com/wp-content/uploads/typescript-1280x720.jpg
---


## Introduction

A few days ago I was refactoring code and I found something like this:

```ts
interface IRawUser {
  first_name: string
  email: string,
  id: number,
}

interface User {
  name: string,
  email: string
  print: () => void
}

declare function userFactory(rawUser: IRawUser): User;

function mapRawToUserObject(rawShow: IRawUser[]): User[];
function mapRawToUserObject(rawShow: IRawUser): User;
function mapRawToUserObject(rawShow: IRawUser | IRawUser[]): User | User[] {
  if (rawShow instanceof Array) {
    return rawShow.map((raw) => userFactory(raw));
  }

  return userFactory(rawShow);
}
```

The function `mapRawToUserObject` uses overloads to express the following: If we call it with an array of `IRawUser[]` it will return an array of `User[]`, if we call it with a single `IRawUser` it will return a single `User`.

Nothing too complex but it seemed like a good opportunity to refactorize the method definition.

## Creating a conditional type.

We need to take a decision on the type of input that the method receives.

The possible inputs we need to look at are: `IRawUser` and `IRawUser[]`

```ts
type MapRawResult<T extends IRawUser | IRawUser[]> = any;
```

Then we add the conditional logic:

```ts
type MapRawResult<T extends IRawUser | IRawUser[]> = T extends IRawUser ? User : User[]
```

> In plain words: If `T` is assignable to `IRawUser` return `User` otherwise return `User[]`

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fguagfly8y5zmoh75rd3.jpg)

## Applying the conditional type to our method.

Right now our method looks like this:

```ts
function mapRawToUserObject(rawShow: IRawUser[]): User[];
function mapRawToUserObject(rawShow: IRawUser): User;
function mapRawToUserObject(rawShow: IRawUser | IRawUser[]): User | User[] {
  // implementation
}
```

We can now remove the overloads, add a generic parameter (`T extends IRawUser | IRawUser[]`), and replace the return type with the one we recently created.

```ts
type MapRawResult<T extends IRawUser | IRawUser[]> = T extends IRawUser ? User : User[]

function mapRawToUserObject<T extends IRawUser | IRawUser[]>(rawShow: T): MapRawResult<T> {
  // implementation
}
```

At this point, typescript will complain about the type of value that we are returning.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/byzizm2mmulwvnkjbees.jpg)

The compiler can't infer the type of the value we are returning, a solution for this is to explicitly express the type we are expecting using the keyword `as`.

Our final method then looks like this:

```ts
function mapRawToUserObject<T extends IRawUser | IRawUser[]>(rawShow: T): MapRawResult<T> {
  if (rawShow instanceof Array) {
    return rawShow.map((raw) => userFactory(raw)) as MapRawResult<T>
  }

  return userFactory(rawShow) as MapRawResult<T>
}
```

## Conclusion

If we try with some values, we can see how our method behaves the same as before but without using overloads.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/17suriv2nkiptu1kx4hq.jpg)

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bmyucy2aggby1qgiqhvy.jpg)

As a bonus we could use a type alias for `IRawUser | IRawUser[]` and make the code cleaner:

```ts
type MapRawArg = IRawUser | IRawUser[];

type MapRawResult<T extends MapRawArg> = T extends IRawUser ? User : User[];
function mapRawToUserObject<T extends MapRawArg>(rawShow: T): MapRawResult<T> {
  // ...
}
```


## Further Reading

- [Conditional Types](https://www.typescriptlang.org/docs/handbook/2/conditional-types.html)
- [Type Assertions](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#type-assertions)
- [Generics](https://www.typescriptlang.org/docs/handbook/2/generics.html#handbook-content)

- [TypeScript Playground](https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgJICU4HcCqBnaZAbwChlkZgo8wB9EOAWwgC5kapQBzM5CRuMAA2bDtwA0vYABM2IAK6MARtEkBfEiVCRYiFPkKlyDZqLCcQXSeX6CR7c914AHC2DYAKAJTIAvAD5kADcAexkSDRJpCAQhOCgUGHkQBDBgEJBkeQIoADFEMBCoAE8PKGwDKDYMCpyvNkqAbk0wYucUAFk4Z0wsAEEoLj80XsrkAB8R2ugAbQBdZtb25C6e7HQIPHkhMAAeABU+AA9IEGk8Fe7ega5A32RDiBOIM4ua3BzkAH5kMYac+bNTRJFJpDLIARrLD7EKVADySgAVjE9o9nq9LlCbv4ytgAMoACxCWDY+3qmN6Gy2OwOgSMyGAMGQuKwhOJDJANDgKQgISZA3KxR89PICTA8igmXKrKJWAAdJCPCyfAEsjl8qkiqVpV4fHALqtKZttqj-LxIqKIOLJWroBrCiUWWysHqDVd1saafszZEEBkaOxuEIIJVqqNPvd6ZRqHQTKxkABybnRKAJ6x8ATCNhJs7QAACRJCQmAcr9jDTUlkyAAjBFNJDejD4UiUR48EGQ3UgX7OWBkPFBaGph9ZnNhjMo1QaPQmPGcymKzZM-Z5-nC8XSyFy+mZGxa2o5vX3dDYTkEcjUh4B3BipUvCQgA)