Type Transformations Workshop (8 exercises)
solution

Using Generic Context to Avoid Distributive Conditional Types

We're going to have to adjust our current mental model of conditional types to understand why we have this issue with distributive conditional types.

For reference, here is the failing code from the problem:

Loading solution

Transcript

0:00 The solution here is pretty weird. It means you need to break a little bit of your mental model when it comes to conditional types. There is a difference between a union type that's expressed as a type here and one that you can get via a generic, so one that you pass in via a type argument.

0:21 Let's actually look at the second solution first. Inside here, we're basically saying we've got GetAppleOrBanana T. We say that if T extends apple or banana, then return T. Otherwise, return never. This means that we're able to extract apple or banana from inside Fruit there.

0:40 If we add in another one here, let's say a pear, for instance, then we still end up with apple or banana. If we remove one of these, then we end up with just apple. We're basically extracting the element from there.

0:53 Inside the problem situation, this doesn't work because we seem to be still hitting never here. Apple or banana is never in this situation, which is really weird. The reason this is happening is when you use a generic like this, when you pull it out into T, then what happens is the members of the union distribute across it. T comes to represent each individual member of the union type.

1:26 What we can say is, does T extend apple or banana? Sure, that works for apple. That works for banana. Doesn't work for orange. Doesn't work for pear. It maps over them and filters through them. You can think of this as like iterating through the members of that union type.

1:41 If you don't do this, then this is no longer in a generic context. This is just itself Fruit. It compares the entire thing against the entire thing. It says, "Does this contain apple or banana?" It does contain apple or banana, but it also contains orange. What we end up with is never.

2:02 This is something that will just catch you out a bunch of different times if you don't know about it, which is why I'm teaching it to you here.

2:09 There is a really weird solution if you want to do it without a generic context, which is if you say, "Fruit extends infer T" -- this is a conditional type, and you're just inferring the thing that's in the Fruit -- now it will be treated as though now you can iterate over it because it's in a generic context.

2:29 This T now is basically acting as the iterable. You iterate over. We check against apple, check against banana. If we do, then we return T. A really, really weird property of union types and conditional types that I really wanted to cover.

2:45 You'll know when you need this. You'll know when this situation comes up because you'll just be thinking, "Why on Earth isn't this working?" You'll realize that, "Oh God. OK, it's distributive conditional types. This one I should put in a generic context first. Then I can mess about with it."