Using Generics with Components 11 exercises

Type Inference with Generic Functions in TypeScript

The Simplest Solution

Let's start with the simplest solution, which might blow your mind as to how easy it is.

Similar to what we've seen before, we can add a type argument of T to the useStateAsObject function:

export const useStateAsObject = <T>(initial: T) => { ...


Loading solution


00:00 Okay, let's start with the simplest version of this solution. This is going to be so simple actually, it might blow your mind as to how easy it is. What we want to do is similar to how we declared a type argument on our function before, is to declare one here. Now, what we do is we just replace the initial,

00:18 which is like the thing that we want to infer from, instead of any, that's going to be T. Okay, now all of our tests are working. How on earth does that work? You notice that now value is typed as T, set is typed as react.dispatch, react.setStateAction, T,

00:38 and value and set down there are all working. And our example is typed as value nameString, and nameString has found its way into here too. TypeScript is pretty clever sometimes. What we can see that when we call useState as object, what we're getting is this little type argument

00:57 is being parsed somehow. So it's almost as if that we parsed in manually this type argument, but we didn't. We're not parsing in that type argument. Here's the thing that TypeScript does that makes this stuff so, so clever. If you don't parse in any type arguments

01:15 into a slot where a type argument is expected, it will try to infer the type argument from the runtime arguments. So now, because we're parsing in this name mat here, that will be inferred into the type arguments. If I parse something else into here,

01:32 like, which is gonna be a number, then that is going to be inferred in there as well. And now, if you do this way around, if you say nameString inside here, then actually this beats the runtime version. So if you, TypeScript only does this

01:51 if you don't parse in any type arguments there. But this is so incredibly powerful. And you notice how smart it is too. TypeScript sees the initial is a T. And so what it does is it parses T into useState here. And now this initial state is T,

02:10 value is T, set is the thing that wraps T. And so useState is a generic function itself, but because it's being used in another generic function, it infers all of the stuff it needs to do from there. Incredible. So there are a few more solutions here. So we could, if we wanted to, manually annotate the return type.

02:31 Because you notice in this first one, we're not actually annotating the return type. We're just letting it pass through. And this, by the way, I think is not as good a solution because you see quite how verbose you have to be to capture something which can be as simple as just this.

02:49 This is just beautifully simple. Let's try a couple more. This one, we can extract it out into its own type helper, right, if we want to. useState is object return, where we just grab that out. You notice that, again, we're seeing that thing where different type helpers parse their types into other type helpers. So this function instantiates T

03:08 and then uses T in the initial here and then parses it into useState is object return name string here. Pretty cool. We can do this with an interface as well, same deal. Literally no difference really in terms of this particular behavior. Or we can, the final thing here is you notice

03:25 that you can manually parse T into useState here. That's cool. So here what we've got, useState, it's actually being inferred as T because we're just parsing in initial as T here. But we can parse in this T manually just to kind of like double make sure, I guess.

03:43 But even here, I think the first solution that we've got is the cleanest because it just makes sure that like, I mean, it's a minimal, minimal setup here but everything still works and works beautifully. So here we're starting to see just how powerful generics can be. And we've gone through the three ways

04:02 that they mainly happen is in generic types, is in parsing type arguments to functions. And then thirdly, it's inferring type arguments in functions from the runtime arguments. Oh, generics are awesome.