Map over a union type

Sometimes you may need want to type an object which has several different optional keys, but all with the same type. We can map over a union type to accomplish this instead of chaining | in an object type.

I start with some code that looks like this

Let's start improving this. First I will make a FruitCount type out of my fruitCounts object, then I'm going to make a new type called NewSingleFruitCount.

What this is doing is its taking each key in FruitCounts and setting its type to an empty object. So when I use it, TypeScript will be expecting a shape like this.

But, I want the properties to be numbers.

As you can see, if I just do that, I still haven't quite got a union type. I get this odd nested structure, and I will still have to have every possible fruit present.

So, what I'm going to do is map over our NewSingleFruitCount type to get rid of those parent keys, and fully create my union type.

And with that, I can now assign our singleFruitCount how I'd like.

Transcript

I've got a puzzle for you. I've got three letters at the top here that are all expressed as a union. This letter can be either one of A, B, or C. I want to create a type helper to remove one of the letters. How do I do that? You think of, when something is an object or something is an array, you can usually map over it. There's some kind of iterator you can use.

What's the iterator when it comes to unions? How do I map over each member of the union? It turns out that TypeScript does this automatically, and this what's called distributivity in TypeScript. The way this works is we can actually treat these letters as though they were one thing, as though they were, for instance, just C, let's say.

The way we're going to do that is we're just going to check T type. We're going to check if T type extends C, and if not, we're going to return never, because never means that it can never be C. We're trying to remove it from union. When you add never to a union, it just removes itself, basically.

Then, if it doesn't extend C, we're going to return T type. Now, while without C, it's just A or B. Now, that's unexpected, because you would think that T type, C is part of these letters. You would think, "OK, T type does extend C, because C is included, so let's return never."

No, actually, automatically maps over each member of the union when it's doing a conditional type check like this. This means that we can return different members of the union or manipulate them. We can actually just return D, for instance. Now, while without C is A, B, or D.

This is pretty exciting, because it means that you can really tear into unions and do lots of really smart things with them.

Mapping over a union type can feel tricky to conceptualise. But actually, TypeScript does it all for you - using Distributive Conditional Types.

Here, we create RemoveC - a type helper to remove c from a union of letters.

Discuss on Twitter

More Tips

Transcript

I've got a puzzle for you. I've got three letters at the top here that are all expressed as a union. This letter can be either one of A, B, or C. I want to create a type helper to remove one of the letters. How do I do that? You think of, when something is an object or something is an array, you can usually map over it. There's some kind of iterator you can use.

What's the iterator when it comes to unions? How do I map over each member of the union? It turns out that TypeScript does this automatically, and this what's called distributivity in TypeScript. The way this works is we can actually treat these letters as though they were one thing, as though they were, for instance, just C, let's say.

The way we're going to do that is we're just going to check T type. We're going to check if T type extends C, and if not, we're going to return never, because never means that it can never be C. We're trying to remove it from union. When you add never to a union, it just removes itself, basically.

Then, if it doesn't extend C, we're going to return T type. Now, while without C, it's just A or B. Now, that's unexpected, because you would think that T type, C is part of these letters. You would think, "OK, T type does extend C, because C is included, so let's return never."

No, actually, automatically maps over each member of the union when it's doing a conditional type check like this. This means that we can return different members of the union or manipulate them. We can actually just return D, for instance. Now, while without C is A, B, or D.

This is pretty exciting, because it means that you can really tear into unions and do lots of really smart things with them.