Some funky parts of Typescript
Tags: typescript
Published: 2023-02-21 09:24:11

---
title: Some funky parts of Typescript
published: true
description: 
tags: typescript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gemyahbk5dhua9he7hta.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-02-21 07:35 +0000
---

And by funky I don’t mean like the smell of chunky milk. I mean funky like the [sweet music](https://www.youtube.com/watch?v=OAC4ItP0xWM) :dancer:.

At [Hotjar](https://www.hotjar.com/), we have bi-weekly Typescript pair programming sessions where we try and solve a few of the [type challenges](https://github.com/type-challenges/type-challenges) together. In one session after solving the challenges we set out to, we chose a [random challenge](https://github.com/type-challenges/type-challenges/blob/main/questions/00020-medium-promise-all/README.md) as a last hurrah and boy was it a doozy.

So why are you reading this? Well let me tell you what you can expect if you take the red pill with this **TL;DR**:

1. First I will take you down the rabbit hole we went on together in the session until we ran out of time.
2. Secondly, I will take you deeper down the same rabbit hole I went on alone the day after.
3. I will then finally share the solution that I shamelessly looked at after being thoroughly defeated by the challenge
4. The main reason I share all this with you is that it contains some interesting nuggets on how the type system works underneath.

### The challenge

As I mentioned already, we chose to do the [Promise.all ](https://github.com/type-challenges/type-challenges/blob/main/questions/00020-medium-promise-all/README.md) challenge. Basically this:

> Type the function `PromiseAll` that accepts an array of PromiseLike objects, the returning value should be `Promise<T>` where `T` is the resolved result array.

so:

```ts
// when passing this array
PromiseAll([Promise.resolve(3), 42]);

// PromiseAll's return type should be this
Promise<[number, 42]>
```

### Recursion

Our first thought was to recursively go through the passed array and return the awaited value of each for the result. Something like [this](https://www.typescriptlang.org/play?#code/PQKgBA+l09YJoHsCuAnMBhRATApmACV1X1jKjBGAChqAXATwAd8BBAdwEMBLO3bAAqpEAW24BnXKwA20gEq5xyaXQA8AFTC4AHnwB22cWBKdsiPdIZhOehgG0AugD4wAXjCad+w2Dvc9AGbEHgBi3KjidAA0YAB08f5B6OoKkQ5g1GBgAPy+HDx82BphEXROMfGx+bz8QqISUrKpymopimXpmWAAXL6V1YUaTg4A3LR4AMbSnCRgAch6E3Tc5mB1YpIy0hpaurgGRiZmFlY29s4AFABunNLIir3qAJS96w2qA7XCG43yii1DJxjaigSDkWAedqYTiSIzgiFUajcERMRCoOhgRgsMAAbzAAFEAI7IW4xfHaFhLMAAXzm3zAAHIAAJY3AAWgmAAtbtJ9gBzRTAZDLaTiBm0CbmSJgJjfBpbdTtACMbjWcs2sgudiVMQATDEAMzpGFgSV6SJPahm6Wy+oa6SKyK61Vve1anVgfVqu24WIkcSIaRXXAXA1PY1Ga10S1RmXq36OugGl3xrbuvUxV2+-2B4Oh8MxqUY20-BXtAAsKZ9Ww+qFQnAYqj0yBEACNggAfb0-Jst9uoJyD9Oew0OS30Zj4CYwxSquxdcmUtREknbVmIAJx6uyRMerOqbUZsBGwflBcU3BLVQr26qdebkvynftL37w8jsDNtvEZyDqLnpdr2JW97y3Utn0iA1M3jA8v37GI4OIBC+x-U9-yyRdL2XYC10nDcwKfB0K2gn1e2-VBHDQ6gHGoIA):

```ts
declare function PromiseAll<T extends readonly any[]>(values: T): Promise<AwaitedPromiseAllResult<T>>;
```

Let’s break this down:

1. Introduce a generic `T` with a `any[]` constraint that we can manipulate
2. Assign `values` to `T` and wrap the result of `T` in our custom `AwaitedPromiseAllResult` type
3. Within `AwaitedPromiseAllResult` we do the following

```ts
// spread T so we can get the first item in the array and the rest
type AwaitedPromiseAllResult<T extends readonly any[]> = T extends [infer TFirst, ...infer TRest] 
 // Unwrap the promise on the first item and repeat this process for the rest
 ? [Awaited<TFirst>, ...AwaitedPromiseAllResult<TRest>] 
 // when there is only one item left in the array, unwrap it and you are done
 : [...Awaited<T>];
```

When testing our `AwaitedPromiseAllResult` type separately it seemed to work:

![Result when testing AwaitedPromiseAllResult in isolation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/i8n8pscq9kk6bl8kxim6.png)

but when using the type in the function we get a whole different result… why, Why, WHY :rage:

![Result when testing PromiseAll function](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pp6eiax0mf9l3z4i4obs.png)

#### Here is why:

Note that we constrain our generic as an `any[]` and then assign our `values` param as that. In other words `[1, 2, Promise<3>]` would become `number | Promise<number>[]`.

Okay, that makes sense. So what if we spread the `any[]` as the `values` type instead? [Well](https://www.typescriptlang.org/play?#code/PQKgBA+l09YJoHsCuAnMBhRATApmACV1X1jKjBGAChqAXATwAd8BBAdwEMBLO3bAAqpEAW24BnXKwA20gEq5xyaXQA8AFTC4AHnwB22cWBKdsiPdIZhOehgG0AugD4wAXjCad+w2Dvc9AGbEHgBi3KjidAA0YAB08f5B6OoKkQ5g1GBgAPy+HDx82BphEXROMfGx+bz8QqISUrKpymopimXpmWAAXL6V1YUaTg4A3LTUeADG0pwkYAHIepN03OZgdWKSMtIaWrq4BkYmZhZWNvbOABQAbpzSyIq9x+aWffHqDgCUvRsNqgO1YSbRryRQtIZOMbUUCQciwDztTCcSRGOHwqjUbgiJiIVB0MCMFhgADeYAAogBHZB3GJk7QsZZgAC+8yBYAA5AABQm4AC0kwAFndpAcAOaKYDIFbScTs2iTcyRMBMIENbbqdoARjc61VW1klzsmpiACYYgBmdLIsAKvSRT7UW1KlX1fXSDWRE0635uw3GsBm3Wu3CxEjiRDSa64S7mz5WoxOugOxPKvUgj10c3etPbP2mmI+kNhiNRmNx5OK-Eu4Hq9oAFmzwe2-1QqE4DFUemQIgARsEAD5B4Gd7t91BOCd5gMWr60Hk25GKHV2Lp0hlqSnUnY8xABVNN2QZ-2F1RG-NgS0T8qr+m4ZaqTd3VQ7vfVtWH9qBk9n6dgLu94hnAnKIb3XB8qSfF99xrD9InNAs01Pf8xxiZDiFQ0dAKvECsjXO8Nwg7dmFwXdoPfd16wQ4MRwA1BHGw6gHGoIA) :slightly_smiling_face:...

```ts
declare function PromiseAll<T ...>(values: [...T]): ...;
```

![Spread type as a tuple](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sx8oobeehhb3ogxx79dy.png)

![Yes](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6g4nnn391ampxb1m2a0.gif)

Awesome that works, but what happens if I override the generic constraint with something else?

![Pass generic override](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1p6uk87wnzned452bb06.png)

![Flipping table](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1uqw282zvcdm88dwwmok.gif)

### The solution

This is where I gave up and looked at the solutions. All of them were basically a different flavour of the same thing. But before I just show you the solution allow me to stretch this post unnecessarily out a little more.

We’ve come to the reason that I was implored to share this with you all. As you’ve seen in the previous section, by spreading an `any[]` type you get a tuple. Which is an array of known types. And as you might know, an array is an object. Why is this important? Because Typescript is smart enough to treat it as such and allow you to map over the indexes of an array like you would the properties of a key/value pair object :exploding_head:.

Or in code words… [basically this](https://www.typescriptlang.org/play?#code/C4TwDgpgBAsghmAggJ2XEiDOB5ARgKwgGNgAVAewEkA7AEwgA8JMAeUqR4COzKOakAG0AugD4oAXigBvAFBQog0gGkIIKAEtqUANZryAMyilhALmOqQsgL6zZoSFC6Zgk2AhRoMOAsTJU6RmYWQQBGABooACZIgHJMcgBbCABaADc4ABsAVwhYsQBuIA):

```ts
type MapArrayAsObjectToIndexes<T extends any[]> = {
  [TKey in keyof T]: TKey
}
```

![demo of MapArrayAsObjectToIndexes result](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8t52gw27im7ozhjthqw.png)

![WTF](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6p0wjh7uu490d1n2jvdg.gif)

Yup :slightly_smiling_face:

And finally [the solution](https://www.typescriptlang.org/play?#code/PQKgBA+l09YJoHsCuAnMBhRATApmACV1X1jKjBGACg8BjAGwEMSwAzZAOzoBcBLRJzAAFVIgC2fAM64AggwYAeACphcADx65O2KWCacAngG0AugD4AFADcmDZLikAuMCSbZBDQ2GMA6f8qmAJQuohLSuIoA3tRgPgDWYHxC8biGiGxggS6yAO5MfFrYKsbxFtQAvuZg1NSgkOSwWY48mEwyeo1NVNR84gAOiKitPIb9+FFgAKIAjsh2ADTT6uO8YBXsYuJgAOQAAqPjALR0ABZ2DNoA5o7AyPwMUju1dIJSrf1bEfIMyi0AjGAALwiL4yH6WYz-JYAJiWAGZTPo9K9OO8gtRUe8wJ9wuCFH93jDgaC8XIFJDoWA4aTJDJfCQpIgGNZcJZ4UEke0wFieBjeTiweTfi14SSwnThZTYUsJREGY5maz2Zz+W8PkKfoSeAAWcWahSKWSoVBMQyKTjIcQAI2IYAAPrSIharbbUOYPdLqQjgrVDvg6O1HCTjLFlqseIpZvMlP6MoKyVqAbKhYooTKwIiPeYFmGpitcLwo3M7Io45lcZKk0SU2S01SaZabcQLB7c3F8xHizGy2NcPHK98CaLa5K0023UsJ8Qp66W9n2+HC5Ho6Xywmq8P3jrR87p6gzAvqKZqEA):

```ts
// as before use a generic `T` and convert it to a tuple for `values`'s type
declare function PromiseAll<T extends any[]>(values: readonly [...T]): Promise<{
  // map over the indexes of the array and return their unwrapped values 
  [k in keyof T]: Awaited<T[k]>
}> 
```

### Conclusion

Typescript is fun to play with. And I know a lot of people might have already known all the things that I wrote about, but if there were only one or two people who found this interesting I will feel satisfied. But the bigger reason I shared this is to encourage you to play around. Understanding how Typescript's type system works has greatly benefited our day-to-day work even though we don't implement things in this way directly. So have fun and keep learning.