Making TypeScript Stick

Variadic Tuple Types

Making TypeScript Stick

Check out a free preview of the full Making TypeScript Stick course

The "Variadic Tuple Types" Lesson is part of the full, Making TypeScript Stick course featured in this preview video. Here's what you'd learn in this lesson:

Mike discusses the recent change to TypeScript, which allows variadic tuple types. A variadic tuple type is a tuple type that has the same properties, defined length, and the type of each element is known, but where the exact shape is yet to be defined.


Transcript from the "Variadic Tuple Types" Lesson

>> All right so the next thing we're gonna do is we're gonna talk about recent things that have landed in TypeScript. So I begin a lot of the TypeScript courses that we have on the Frontend masters platform with a quick update to what's new to the language. Aside from TypeScript fundamentals and intermediate TypeScript, those courses kind of start at the very beginning.

It's good to be able to stay up to date and I want you to know, kind of what are the things you should care about? What are the things that impact you as a programmer that have landed recently. So, this represents stuff that has landed since the 4.0 release.

And why have I chosen that is sort of where we begin, It's really where the expressiveness of types, has become a lot more powerful. It's worlds different using TypeScript today, than even a year ago, there are things that couldn't be done, which are now possible. And we're going to talk about these topics here in this list.

These are six things of the 30 or so things that have landed in the language, six select things that I think will make a difference to the way you write your code. A lot of the stuff in the release notes you might not. Don't care about you little fixes to the compiler performance improvements everybody likes those I don't need to tell you that performance is better that doesn't change the way you write your code.

So the first thing we'll talk about here is variadic tupple types so it's a huge deal. So first, as is sometimes the case, in the TypeScript world, we have this complicated terminology. It's a little bit scary. What does variadic tupple type even mean? Why should you care about it?

And I promise we'll answer that question by the end of this little section here. So, I want to work our way up to understanding what's the type, what's the tableau? What were the limitations? What can we do now that's different and why does that matter to you? That's kind of our order of operations here.

So, first, if you've never worked with tableau types before, they're great. They're kind of think of them almost like a fixed length array where we know the type at any given position. So instead of having an arbitrary array of numbers, we could have a tupple of length three that represents a color, each being an eight bit number, so like 00255, that could be red, right?

And we can pass around this array and there's sort of a special meaning that the first position second and third that's the R, G and B channels respectively. So we could use an object for this as well but sometimes there are advantages to using tuples instead. And for a while, we've been able to use a spread operator as the last element of the tuple.

So I'm going to set the stage with a more interesting example here than color, where a tuple to me makes a lot of sense. So let's imagine we're running a deli and we sell a bunch of different types of sandwiches. So here's an inn umm for sandwiches. So these will each be a number effectively.

Like you can see hamburger equals zero. Veggie burger equals 1, right? And then we have a couple type that represents a sandwich order. And you can see here in the first position we have the order total, in the second position, we have the sandwich and let's pretend. And at this deli, you can only order one sandwich at a time.

Just to make my contrived example work. [LAUGH] So you can have one sandwich followed by an arbitrary number of toppings, and that's a string array. So this tuples going to look like, number, and then a sandwich and then any number of strings could follow. So if we were to create a couple orders here, we've got one that's a hamburger with lettuce and a 1299.

And then we've got a second one and you can see like the price is a little different maybe we're like up charging because we're using avocado here, but you can see we can have multiple strings. And if we look at this you can see in the tooltip This is a tuple it does not necessarily have fixed length, but there is some understanding we have about the types at each position in the tuple.

And you can see, like type checking works the way we would hope it does. If we forget the sandwich as we have in this last example, we can see that type checking tells us hey, I didn't expect to find lettuce here. So this is, this is an example we'll be using, as we understand more about variadic tupple type.

And the key here, the thing you probably have not done before is this having a rest or a spread element as that last thing in the tuple. It has been possible but an unusual to use it. So it's even possible to use this in a generic way. And if we look at this, you can see I've got this type T and I can have a number followed by any number of T's that I care to put in there.

And so here you can see like we've got the string case and the Boolean case. The important thing is you have to use at least before TypeScript for before these variadic tupple types. You absolutely had to use T as the member of the array. And then you had to say, we've got dot, dot, dot and an array of T's.

This is where we encounter a problem. So let me show you what you would have gotten if you tried to do something different. Let's say we wanted to do this a different way, instead of saying, I have a member, membertype, and here's an array of those members. What if we wanted to say T is an arrayish thing.

And I want to do ...T instead, in TypeScript three, you would have gotten this error. It would have said and by the way this is a terrible error message a rest element type must be an array type and over here like this looks like an array type to me.

This is in fact any array, right? So this has been fixed but Let's talk about why this matters why should you care. It seems like we've drilled way down we found this little like exotic specific failing case why should you care so returning to our sandwich order. We've created an order here hamburger with lettuce and let's say that we have a function we use to pass our order to the kitchen.

And the kitchen does not care about how much this costs they just care about what is the sandwich, what are the toppings I need to put on the sandwich? So let's say we have a function that is meant to get the tail of the array and if you've done any functional programming before.

Tail is probably something that you're familiar with, it just all we're doing is we're taking an array in and this allows us to do this with read only arrays as well. So, if you're using immutable data structures of some kinda, this would also work and you can see here, the first thing's a number.

We have an array of Ts following that. We can kind of chop off this first thing and ignore it. Get everything else in the array and return that everything else. So effectively we're like chopping off the first thing giving you everything else and look at what we get.

I would argue that in this situation, we have lost information. We get instead of a nice tupple type, we get an array type that says. This is a shuffled mix of maybe some sandwiches maybe some toppings. My argument here is, we have lost information, right. So if I give you a deck of cards and I say there are four aces on top.

Here you go compared to like just mixing them up down here and handing them to you, you know less about that second deck of cards that I shuffled. So this is less information, it's less specific. Where this trouble happens is it has to do with this thing here, right?

This is where we're saying. I need I have this type t, an array of ts, right? And I need to match that with this. And when this inference happens this could be a mixed array of any length, right? So when we say like, we compare these two things and the left and the right and we say, what is T?

Well, it could be either of these things. This is where things break down and where yeah, this isn't a valid answer to the question, but we lost information in doing this. So like, here's another example it simplifies things even further this function here all it does is return the array you give it.

But in doing the same kind of inference Is where we pass in this array or this topple, so it comes in here. And we return the same thing you can see in the tooltip, that T is being regarded as sandwich or topic, right? Hamburger or lettuce. So that's where the problem is happening.

That's where we're making that type equivalence check. What we want instead is something like this. So here, we're going to our different scenario where we're saying, I have an arrayish thing Type T is some type of tupple or array, some ordered collection of some sort. And I want to infer T, not an array of Ts, T.

Things work out much better for us here. You can see it's a very similar function. It's literally the same implementation here, right? We've got just return arg. But just the way we've expressed, is the type the array or is the type the member. That makes a huge difference for us here, we don't lose information now.

And this is the small change that we can make to this tail function. So, we have been able to do this the whole time. This was possible in TypeScript three on a function, but not on tuples. So now, we can do this, where we go from this is T, I'm taking an array of them to this is an arrayish thing.

Infer what that thing is, that's happening over here. And now, we get the same nice result out. So this means that when the kitchen gets the order, when we chop off that 1299, now we've got, here's the sandwich, here are all the toppings that come off of it.

So why does this matter? I've given you one example and we can see in the tooltips we get a different result. If using everyday TypeScript code where might you see this? Arguments, function arguments types are often going through the type system as tuples. So, if you're trying to get like the arguments of a particular constructor or something like that this is where you would have gotten kind of a jumble of things.

Whereas now you've kind of preserved the order if there's a rest element at the end, you'll get the correct type for that. And you may not be creating types like this in your own code but libraries you use might be sort of feeding them to you. And this will have an impact on what works in your code and what doesn't and your ability to catch bugs before they hit production.

So this is one of those lower level things seems like a subtle fix, but it has pretty massive ramifications. Another thing that you can do is multiple spreads in tuples. So that's part of this feature set. And we have greater flexibility around Rest elements. So this is something that you don't often see but it's possible, you could say I have a tuple that's a Boolean.

And then any amount of numbers you like but it must be followed by a string. So hopefully, this kind of explains why you think about this type. We think about these as fixed length things where it's just number, number string, something very simple like that. But there's a lot of flexibility here and this opens some major new doors in terms of how we can express things in type script.

So, as a practical example, to connect this further back to reality, who has used RX JS before or heard of it? Okay, a couple people. So the idea behind RX js is there are these things called observables. You can think of these as like little beacons that emit events, almost like a promise that could resolve more than once.

Where every time you move your mouse or every time you click a button, this thing fires and you create a chain of things that attach to an observable. And that's usually where you'll respond to these kinds of events. So I told you that function arguments are where you can see this tupple type in action.

Here is a pull request showing the back flips that, that the RX JS folks had to do in order to deal with the limitation of not having these variadic tupple types. You can see they basically had a separate type signature for the one argument, the two argument, the three arguments for argument five.

All the way to the sixth argument flavor of this function and they're able to replace that with a couple things right. We no longer have like T one through six we can just say look now that the tupple works the way I would hope it does If we can shrink all this way down we can simplify.

So even if you aren't writing this code, this is gonna be much faster much less confusing when you see those little tool tips pop up like you don't have to now look at. That this tooltip says there are 17 different options for calling merge like You're looking at 3 of 17 and you're sort of clicking through to figure out which one you should use.

Now, everything can be more concisely represented into a fewer number of signatures that kind of just work. So your documentation is gonna be better t things will be simpler and easier to read and understand. And you can see like they're still working on cleaning these up but you can see elsewhere, all of these places where they've got T T2 T 3 T4 T5.

That's the code being removed and what's added is, these tupple types with this spread operators because they that now we'll grab the information the way it should have worked.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now