When working with conditionals types, within the “extends” expression, we can use the “infer” keyword to either get the type of the elements of an array, or even to get the return type of a function. We can use this to build a “FnReturnType<T>” type, that will give us the return type of the function passed in as the generic parameter.
Instructor: [00:00] Typescript's inference engine is really good. I can build a function like this one and without declaring any return type, I can immediately start to safely use it. If I assign its return value to a variable here that only stores strings, compilation will fail, which is great.
[00:18] I can save some space in my code and I'm also not locking down my function to a specific type. I can leave it open to changes in implementation while still maintaining confidence that it's still being used as it's meant to and if I do have to make any refactoring changes I know where to do that.
[00:35] If I change this to a string now, the return type of this function also changes and this now passes compilation just fine. What if I want to build another function here that I know will be called with whatever result might generate ID function returns.
[00:50] I can look at its implementation and see that it currently returns a string and I can hard code that type in here. Because of its dynamic nature, it might change, which will break the assumption that I've made. This now fails compilation.
[01:04] I need to somehow create a life link between this type and the return value of this function. Let me create a new type here. I'll call it return type with our good old friend generic parameter T. When using conditional types, you also get access to an infer keyword.
[01:22] In here if T extends a function with a variable number of arguments, infer its return type and store it into R. Then return R, or if T doesn't extend the function, just return any as a placeholder. If I create a type, I'll call it ID, which is going to have the return type of the generate ID function.
[01:44] Now if I hover over it, I can see that it's correctly using whatever this function returns. If I change the implementation of this function, the type of ID also changes to a string. Instead of using a string here, I can just use whatever type ID is.
[01:59] This type that we just built can also come in handy if you're using a third party library and they export a function but not its return type. You can just extract it yourself using this. This type is actually so useful that it now ships with Typescript since version 2.8 so you don't even need to create it yourself.
[02:17] The way the infer keyword works is very similar to pattern matching. I tell it that a type that follows this pattern will be used. It will be a function with an irrelevant number of arguments. It will have some return value.
[02:31] Because I've placed infer R in this location of the pattern where the return value is, it knows exactly how to extract the type. I could have also placed an infer statement here and then it could have inferred the type of the arguments passed in.
[02:47] This give weight to some really powerful applications. I can, for example, create a type called unpack promise that takes in a type that's an array of promises that resolves to a specific value. I will just return the type that those promises wrap.
[03:04] Now if I create an array and I'll just put a promise in it that resolves to a Boolean and I pass that type of the variable that I just created to my promise unpacker and I hover over it, I can see I get back a Boolean. This is kind of powerful.
[03:19] I have this runtime piece of code which is an array which contains a promise, and that promise resolves to some Boolean. From this very deep nesting of runtime code we were able to extract the type of whatever that promise returns. All I had to do was give it this pattern that it should work with and then add the infer keywords anywhere in that pattern where I want types to be extracted.
[03:43] In this case as my generic parameter I expected an array and in that array I expected some promises. Then each promise will resolve to a type K. Then I'm asking for that type K back once Typescript is able to infer it from this pattern.