Use infer with Generics to Extract Types from Arguments
Before we get to the solution, let's take a look at another example.
Example2 is calling
MyComplexInterface, and we're trying to return the
For a first pass at a solution for
GetPoint, we call
MyComplexInterface and pass in
any for each of the slots:
If we change
Example2 to call
GetPoint with a single random argument, we'll end up with
never because of the branching logic.
infer to Extract From a Slot
So what we need to do is somehow inside here is use an infer to extract out one of the slots.
The Not-Ideal Solution
One option to make the test pass would be to update
GetPoint to use
The issue with this solution is that it ties the conditional type to the internal structure of
MyComplexInterface. This pattern might not be something that you always wanna do.
The Better Solution
Instead of tying into the structure of the interface, we should instead just look at its public declaration because it is less likely to change.
We can add
infer to any of the slots in the declaration (where "slot" is anything between angle brackets). In this case, all of the slots above are
Point was the fourth argument, we can replace the corresponding slot with
TPoint then return it for the matching conditional branch:
This approach gives us all of the benefits of being able to extract out all the members of the type arguments without needing to dive deep into the element itself to understand its structure.
You could add
infer for the other slots, but in this case
GetPoint is only interested in
Being able to extract type arguments to another type helper is another really cool feature of
[0:00] [0: 00] Let's take a look at the solution here. I've got an Example2 here, which is going to call GetPoint on MyComplexInterface, and we're trying to return this 4 here. As you'll see, it's currently hovering over any.
[0:15] [0: 16] What's going on here is that, inside the GetPoint function itself, what we're doing is saying T extends MyComplexInterface, and we're passing any into each of the slots. If we don't pass any of these, then it's going to yell at us because it requires four type arguments.
[0:29] [0: 30] What we can do is we now know that T extends that thing. If I try to pass in something random like 1-2-blah-blah-blah, then I'm going to get never because it's going to hit that branch there instead of this branch. What we need to do is somehow, inside here, use an infer to extract out one of the slots.
[0:51] [0: 52] One thing we could do is say T, let's say getPoint, return type getPoint. That makes our tests pass. We end up with the 4 here that we're passing in. If I change that to 12, then we get 12, but it sort of ties the conditional type to the internal structure of this interface here, which might not be something that we always want to do.
[1:16] [1: 16] We want to look at the public declaration of that interface, which is the type arguments, which are pretty unlikely to change, or less likely to change let's say. Inside here, what we can do is let's say any for now. We can add an infer to any one of these slots here. Let's say we infer TPoint here. We can then just return TPoint.
[1:42] [1: 43] Now, you get all the benefits of being able to extract out all the members of the type arguments without needing to dive deep into the element itself and understand its structure. We can even do more of this. We can say infer -- what's that one -- if infect TEvent, infer TContext, and then infer T whatever. What you end up with is a bunch of different infers.
[2:12] [2: 12] This has no behavioral difference. I'm just showing you that you can infer multiple slots if you want to. We can return the TContext if we want to or the TEvent or something. Having them as any is also fine because all we really care about in this case is extracting the TPoint. This is another really cool use case of infer because you can just extract out the type arguments to another type helper.