Pushing the limits of TypeScript
Tags: typescript, javascript, webdev, beginners
Published: 2022-08-15 09:53:21

I often get stuck with TypeScript, and when I do, I want to throw my Mac out of the window. So I’ve created this article, to help you and I write better code. I’ll be updating this post when I stumbled upon new and useful TypeScript hacks. In the meantime, feel free to get inspired to create your own cool TS magic, or to just steal some code, I won’t judge… 😜

## First index of array

You might want to get the type of the first index in an array, and if so, this utility type is for you.

```tsx
type First<T extends any[]> = T extends [infer P, ...any[]] ? P : never;
```

It’s a rather simple utility type to help you infer the type of the first index of an array.

```tsx
type Test1 = First<[() => string, "a", "b"]>;
//   ^ Test1: () => string

type Test2 = First<[undefined, 4]>;
//   ^ Test2: undefined

type Test3 = First<[]>;
//   ^ Test3: never
```

As a bonus, to get the last element of the tuple, change `[infer P, ...any[]]` to `[...any[], infer P]`!

If you’d like to use a variable of type tuple, you’ll be better off using the bracket notation to access the first element of the array, like this: `T[0]`.

```tsx
type First<T extends readonly any[]> = T[0];
```

And then pass the `typeof` your tuple.

```tsx
const numbers = [1, 2, 3, 4, 5] as const;

type Test = First<typeof numbers>;
//   ^ Test: () => 1
```

**Note**: remember to cast your array as a const, using `as const`, TypeScript will then infer it as `readonly [1, 2, 3, 4, 5]`.

## Infer the type of each element when using map()

Let’s say you have an array of vowels, or `string[]`. Now, let’s say you want to loop over the array using `Array.map()`. You would have something like this:

```tsx
const vowels = ["a", "e", "i", "o", "u"];

vowels.map((v: string) => {});
//          ^ v: string
```

An easy, yet underused approach is to cast the array to a tuple using `as const`. Doing so will allow `Array.map()` to return the possible values obtainable by `Array.map()`. So now, your code should lok something like this.

```tsx
const vowels = ["a", "e", "i", "o", "u"] as const;

vowels.map((v) => {});
//          ^ v: "a" | "e" | "i" | "o" | "u"
```

Casting your array as a `const` does come with a drawback, however. You won’t be able to mutate it after the fact. That means no more `Array.push()`, `Array.pop()`, etc…

## Getting the length of a tuple

TypeScript is really clever, as it lets us access the prototype of an element. So instead of regular JS, where we use `Array.length`, for TS Types, we can use `any[]['length]` to access the length. And if that array turns out to be a tuple, TS can directly infer the length!

```tsx
type Length<T extends readonly any[]> = T["length"];
```

Using it is simple, either pass a tuple directly into the type…

```tsx
type Test1 = Length<["a", "b", "c"]>;
//   ^ Test1: 3
```

Or, a more practical approach is to pass a variable of type tuple…

```tsx
const letters = ["a", "b", "c"] as const;

type Test2 = Length<typeof letters>;
//   ^ Test2: 3
```

**Reminder**: the above method only works with variables of type tuple, so arrays unfortunately will not work…

## Conclusion

That’s it! I hope this helped someone out there. I’ll be updating this post when I stumble upon some other fancy TS trick. In the meantime, you can check out [this post about Bun](https://reactive.so/post/whats-the-deal-with-bun). It’s worth the read; I promise.

Thanks for reading 😊