Adding Generic Type Arguments to Type Helpers
Alright, let's dive into button group props. As we look into this, we can probably infer that value
is going to need to be a generic type. So, for the time being, let's denote it as T value
.
As we've seen before, we can tell that value
is going to need to be a generic type. We'll denote it as
Transcript
00:00 Okie-dokie. So button group props. We can see probably that value is going to need to be a generic type, right? So let's call it T value for now. T value. And we need to instantiate this somewhere.
00:13 So let's put this on button group props. T value there. Now button group props is also going to need T value, as we can imagine. And this T value also needs to be put somewhere, needs to be instantiated from somewhere. So let's put it there.
00:27 Remove the end of the fragment, go T value, and then we're good to go. So this is working nicely. Now props.onclick here, we can see that argument type string is not assignable to parameter of type T value. T value could be instantiated with an arbitrary type, which could be unrelated to string.
00:46 Oh, okay. TypeScript. Thanks. Yeah, that's true. It could. It could. What it's saying there is it's saying that T value doesn't necessarily have to be related to string, right? So onclick currently receives T value, button value. It's a string, right? It's not T value.
01:05 So how do we get there then? Currently, you can see that value is being inferred as unknown here. That's because at this point, this onclick, it doesn't know what value is supposed to be.
01:19 So it doesn't try to do any inference there. So what we could do is we could just say, okay, add or delete here. And then this would work, you know, this is working fine. But actually, this is not very dry, because we've got add, delete here, and we've got add, delete here.
01:37 And actually, we don't even need to pass in add or delete here. It's not actually being constrained to the other one. So let's remove that. That's definitely not a solution. The way we've got to do this is we actually need to infer it from the type of value here.
01:51 So what we've got is an array of buttons in there. And we need to basically say, okay, this is T value, and this is T value. So let's make this a generic type. T value goes in there. And instead of value here, we've got T value there.
02:08 Now button, it's saying generic type button T value requires one type argument. So let's give it T value. And suddenly, it starts to work. Not quite. I thought it would work, actually, but it's not going to.
02:23 Now the reason that it's not working here is because actually, well, there's like an error up here, which sort of tells us why. T value is not assignable to type key or null or undefined.
02:34 We're using value in this little slot just here to basically type to pass a key, a unique key to the button to make sure that when it's rendered, it sort of doesn't re-render unnecessarily. You guys should know this.
02:48 When you're inside a map function inside of React, you should pass key to the elements that you're rendering in order to get it. So how do we know what type this expects? Well, if we command click into it, we can see that key, react.key is string or number, just that.
03:05 So what we want to do is, because T value can be anything, we actually want to constrain it to something that maps onto this key. So what we can do is T value extends, and let's just say extends string.
03:21 Oh, now we're getting somewhere. Now we're getting somewhere. So this value now, it's add or delete. So what's happening here is that basically we've got our button group, and let me actually just say const example equals button group,
03:37 and I'm going to call it manually. So by calling it manually, I'm just going to get a little bit better autocomplete here. So buttons, let's just say an empty array, and onClick like this, and I'll say our value here. And I'll actually do this below the button group so we don't get that error. There we go.
03:54 So now value here, we've got value string, so it's being inferred as, it's actually being inferred as string because we're not passing in any buttons. But as soon as we pass in a button, we've got our value, and let's just say wow, and label wow.
04:08 And we've got this value here is now being inferred as wow. So what's going on is that we instantiate our T value. That gets passed into button group props. That then passes itself down into button where it finally finds a place where it can infer from.
04:24 So this is the pattern that I wanted to show you. You're actually inferring your type argument through multiple different helpers. You've got this helper, and you've got this helper inside it. And even though it's down through a couple of different layers, it still manages to get the inference working.
04:40 And we used a constraint to make sure that it mapped onto the value of key here, or the type of key. Amazing.