Type Transformations Workshop (8 exercises)

Two Techniques for Mapping a Discriminated Union to an Object

As mentioned, there are two possible solutions to this exercise.

Solution 1: Creating a Union

One way to solve this problem is based on creating a union of all of the possible route values, which is the discriminator.

Here's what this this solution looks like:

In the code above, we map over the Route["route"] union. Each key is set to the value of route, which is then assigned to R.

We then use Extract to get out only the route that matches the current R value.

Finally, we use an indexed access type to get the search property off of the matching object:

We do end up with the correct solution, but it's too verbose.

Solution 2: Using as

A cleaner solution is to iterate over Route itself, and remap the keys using the as clause.

As seen in the previous exercise, this means that we have access to the entire route object instead of only the route property.

This means we can use indexed access to get the search property directly off of R without needing to do any kind of extraction!

Here's what this solution looks like:

Experiment: Removing the as clause

Let's try removing the as clause from the RoutesObject solution above:

This throws us an interesting error:

This is saying that you can't just have whatever you want as the key. You must use either a string, number, or symbol as a key in TypeScript.

When we use as, we are able to assign R to whatever we want, while ensuring that the key will be set to a valid type.

This is a great technique for manipulating discriminated unions into new objects!


[0:00] Let's take a look at this first solution here, where we have the route, and what we're doing is we are saying for R in route, route, we're taking this as the discriminator. We then say extract route, route, route, route, search. Let's work out what's happening here.
[0:19] If we just take this and we just take a string there, then what we end up with is we've got the discriminator and we're turning this into an object where the keys aren't the discriminator. That's good. That's what we want to do.

[0:33] We end up with R, which is this route thing here, and what we want to do then is we basically want to say type extracted equals, we have access to about, and admin, and admin users, but we don't have access to the search within this iterator. We somehow need to grab the search.

[0:53] What we can do is extract and then say route, and then route is going to be, let's say, about one fact. Let's do the first one, and then we have access to extracted element there, so we have access to the search.

[1:08] We can grab this just by using an index type, which is the search here, so we end up with the correct search. That means if we change this to admin/users, then we end up with the correct thing too. This is admin/users just has this, if we have id number, then we end up with the correct thing here too.

[1:28] What that means then is that we're basically just in-lining this and instead of this, we have an R there. That means we do end up with the correct solution, but it's maybe a little bit too verbose here. There is an easier way we can do this, which is here, this looks just beautiful, doesn't it?

[1:51] We actually instead of the R route being the thing we're iterating over -- if you notice in here, we're iterating over R route -- we actually iterate over route itself. R in this particular case actually represents the entire object, and what we end up with then is we can just access R search.

[2:14] We don't need to do any of the extraction stuff. Our search actually represents the pieces that are in here. I can have id number in here if I want to, and that will make its way into this too, which is extremely neat. Then we just take that R and we key remap it to use the route as the key.

[2:33] This is a delicious way to extract this and make it all work, and it means that you can actually iterate over each individual element of the discriminated union and then just pull out the parts you need.

[2:46] If I were to just remove this actually, we get a really interesting error, which makes you feel like this isn't possible, because actually this, it's not assignable to string, or number, or symbol, because inside here, you can't put in any random stuff here, let's say, or string array.

[3:04] Each of these numbers or each of these values are not assignable to the correct type. String, or number, or symbol, those are the only things that you can use as a key in TypeScript, but actually, if you just have it here and then remap it, so R route, then it lets you do it.

[3:21] This is a fascinating little way to manipulate objects or manipulate discriminated unions into new objects.