Type Transformations Workshop (4 exercises)
solution

Create a Discriminated Union through Intermediary Transformations

The solution here is pretty dense and looks a little complicated:

Let's work through it by going back to first principles, and checking our work by hovering over ExclusiveAttributes:

When written this way, MutuallyExclusive will remap over the attributes of the object that is passed in and return them to us.

We can check this by hovering over ExclusiveAttributes:

In order to create a union, we want to create an intermediary representation of that union as an object. From there, we'll remap over that object to get back our proper union values.

Create an Intermediary Record Type

The members of the union that we want need to be stuck into the T[K] slot that we wrote above.

For now, we'll replace that slot with a new Record type that will take in K and an empty object:

What this says now is for each of the properties of the object passed in, create a record that has the key and a new empty object:

Since we want to represent the value of the thing inside of the attributes, we can swap the empty object with the T[K]. Recall that T is the Attributes being passed in, and K is the index of the key we are currently on:

Since each thing inside of Attributes is typed as a string, that's what we'll end up with now:

Map over the Records to Create a Union

Now that we have id, email, and username available to us, we need to map over them to grab the members of the union.

We do this by appending [keyof T] to the end of MutuallyExclusive:

Transcript

[0:00] The solution here is pretty dense and complicated. You can see this mutually exclusive type. We're obviously doing a mapping type, and we've quite a lot of annotations here. Let's remove a bit of this and go back to first principles.
[0:14] If we just start from this setup, which we've seen before, then what we're going to end up with is it's going to remap over attributes and return them to us.

[0:26] Starting from here, we know that in order to create a union, we want to create an intermediary representation of that union in an object, and then remap over that object in order to get back our proper union values. That's the way to do it.

[0:44] The members of this union that we want are what we want to stick in this slot there, which is in the value. Let's say Record<T>, and let's just say...In fact, it's going to be K, and let's actually just make an empty object here. I'm going to say const example is ExclusiveAttributes. Let's just see what this is here.

[1:07] What this is doing now is it's basically saying, for each of the properties, I'm going to create a record where it has one key, which is id in our username, and then we're going to parse something random into that. Currently, we're just parsing an empty object.

[1:25] I can do this with email, id, id, and then username, username, username. Each of these, we don't want to parse this here actually. We want this to represent the value of the thing inside attributes. That means we can say T [K] .

[1:41] T then is going to be this thing we're parsing in, which is attributes, and the K is the index or the key that we're currently on. This means that each of these then is going to end up being a string, because that's how they're typed inside attributes.

[1:56] That's pretty good. We're getting there, but now we've got each of these available to us, we just need to map over id, email, and username to grab the members of the union. For that, we say keyof T, and now the example that we get at the end is going to be email, blah, blah, blah. That's going to be enough to parse this.

[2:17] The cool thing about this is that I have to parse one of these. This is really nice for like form handlers and things like these, where you have to at least parse one, so we have line of id and that makes it parse.

[2:31] This is pretty cool. We end up with this record of id string, or record email string, or record username string.