Infer Arrays as Tuples

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

Infer Arrays as Tuples

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

Objective

We want to infer a tuple without having to type it out manually like we do below:

const manual: [string, string, string] = ['red', 'blue', 'purple'];

manual[2] = 'green'; // Desired: βœ…, Actual: βœ…

manual[3]; // Desired: ❌, Actual: ❌

Solution

Add the satisfies constraint with at tuple of at least one length:

const tuple = ['red', 'yellow', 'purple'] satisfies [unknown, ...unknown[]];

tuple[2] = 'green'; // Desired: βœ…, Actual: βœ…

tuple[3]; // Desired: ❌, Actual: ❌

General Utility

Here is a general utility for you:

type InferTuple<T = unknown> = [T, ...T[]];

In action:

type InferTuple<T = unknown> = [T, ...T[]];

const tuple = ['red', 'yellow', 'green'] satisfies InferTuple;

tuple[2] = 'green'; // Desired: βœ…, Actual: βœ…

tuple[3]; // Desired: ❌, Actual: ❌
javascript
typescript
react
playwright

Enjoy free content straight from your inbox πŸ’Œ

No spam, unsubscribe at any time.

Transcript

00:00

We've already looked at how the satisfies operator can be used to gently nudge the TypeScript inference engine towards specific types for certain properties. Let's build on that to take a look at another example. And this time we will use the satisfies operator to guide the inferred type for an array to instead be a double. Let's take a look at a motivating example. We have a fixed number of strings, and as we've discussed before, an array with a fixed length is actually called a topple. We should be able to modify the individual items of the topple. For example, we should be able to modify the item at the second index

00:32

to be a different value, for example, green. And this does work as expected. However, since we want this to be a double, that is, it should have a fixed length. If you try to access the fourth item, which is the item at index three, it should actually be disallowed. However, since the inferred type over here is actually an array and not a double, this will be allowed by TypeScript. So let's take a moment and think about how we could achieve this with minimal code. Of course, we could go down the full blown route and add the explicit type annotation. And honestly, that is perhaps one of the best options that you have.

01:04

There's no fault in being explicit. And this does get us very quickly to our desired state, but imagine that you have more items than three. For example, you have a workflow where each item is a step and you might have 20 or 30 steps. You really don't want to be typing out that double yourself. A quick shortcut that you can always take is to use the S conser. However, this will be more restrictive than you might want, as an example. It'll even prevent reassignment of the individual items. So let's come up with another way, how we can guide type script towards inferring a topple without having to write it out ourselves.

01:37

And no surprise, as you mentioned in the beginning of this tutorial, we will be using the satisfies operator for this purpose. After we create the variable, we ensure that it satisfies the constraint, that it should be a topple with at least one member, and optionally other members as well. And we don't particularly care about the type of the members. We leave that to TypeScript to infer and market as unknown. And this actually gets us exactly what we want. The individual items within the top are still assignable, so we can reassign the item at index two to a different value, for example, green. However, any out of bound access, for example,

02:09

accessing the element at index three is going to result in a compiler error as the maximum allowed index for this particular double is index two. And of course, we can verify that it is inferred as a double by hovering over the value. And you can see that it is a double consisting of three strings. At this point. You might be thinking that the statement after satisfies looks a bit hard to memorize and then code out every time. And I would agree with you. So let's move it into an infer type utility, which will make it feel like you are truly extending the TypeScript programming language. We create this utility called infer double,

02:42

and we taken a generic type argument for which you provided a default value of unknown if you want to leave the compiler to infer that for you. And then it is simply going to be an alias for a couple that has at least one item of type T, along with potentially other items of type T as well. And now that we have this utility, We can use it as many times as we want. So let's repeat the example. We have an array consisting of strings, however we want it to be inferred as a fixed length array consisting of exactly three strings. So we add satisfies, inferred, double, and this is not that hard to read compared to as cons.

03:16

And it does give us the desired result. That is, we can still assign the individual members. However, we cannot do any out of bound excess by mistake. And I think that this is a great example demonstrating the power and flexibility of the TypeScript programming language.