Capture and Extend React Select's Type Definitions
Indirection with the way that props are declared can be a problem.
When navigating into
ReactSelect's types, we end up at
StateManagedSelect which has a large function definition:
// https://firstname.lastname@example.org/dist/declarations/src/stateManager.d.tsdeclare ty
00:00 Okay, let's take a look at this. Now, React Select here, there are a few kind of like problems with the way that the types are declared, especially when it comes to indirection. If we take a look at React Select here, I'm just command click on it, this is kind of like our entry point here, command click on it, we end up in State Managed Select, and State Managed Select is State Managed Select,
00:19 State Manager, State Manager Props. Okay, there's a lot going on here, and this huge, great big file kind of like jumps out at us. But we can see that State Managed Select is kind of like leading us in quite a nice way, actually, because we've got a big old function definition, but that function definition, it just returns a React element. And we know what React element is,
00:39 we've used it before in these exercises. So we know that this is a component, and we can see that this component has three type arguments. We have Option, we have IsMulti, and we have Group. And all of these have kind of some constraints on them.
00:56 So we have Option, Unknown, IsMulti here, and we've got State Manager Props available to us. State Manager Props still has Option, IsMulti, and Group here. And we have, again, this massive, great big file with these huge definitions. We have UseStateManager inside here.
01:14 And what we can start to see is like, sure, we've got a Props attribute or Props type coming from React Select. But if we command click on it, it goes to another type which is named something different, State Manager Props. And we could try to understand all of these different pieces here,
01:31 or we could just try to really replicate the function that we know works. And the one that we know works is this React Select. So we have a State Managed Select here. So what I guess we could do is we could try doing this, State Managed Select. And let's just see if that works. Wow, it works. Okay, fantastic.
01:50 So now we have just at stroke fixed all of our problems, it seems to be. So we have Select, React Select, OnChange, Option, that all seems to be working. But there's an issue with this, or at least I can see an issue with it, which is if we type this function here, what happens if we want to add some more props to this?
02:10 Because we're using this function type here, it's actually quite hard to figure out a way to add additional props onto this. Because, I mean, look at some of these types here. We have emit, public, blah, blah, blah. How do we actually figure out what type these props are supposed to be? So I don't see this as a perfect solution here. I think the best solution would be
02:29 to somehow find a type for this. And the type that immediately springs to mind is this props type. So let's take a look at this. We have props. And we can just type it as props because all of these options actually have defaults here. So Option has a default, IsMulti has a default, and Group has a default.
02:49 This is very common in libraries, actually, in order to let users kind of use these in the way they want to. They'll often provide defaults for all of them. And if we take a look back at our React select components here, we can see that this function has a bunch of type arguments. In these situations, we could dive deep into props like we did with React hook form
03:09 and figure out exactly how those are working. But one way that we can actually sidestep that is we can just take these type arguments all the way through here, copy them, and just paste them before our function here. So we have Option equals unknown. We need to import GroupBase from React select. Good, it's nice that it exposes that.
03:29 And now we can pass those into our props that we get from React select because this takes in exactly the same type arguments. We've got Option, IsMulti, and Group. So let's take a look at that. We can pass in Option, IsMulti, Group. Then we save that.
03:47 And now everything starts to work again. So we're now in a position where, if we take a look at this select, we can see here that guitarist is being inferred here. So we've now got this type is single value option. Beautiful. Now, we can take a little look at these
04:05 just to figure out kind of like how they're working. Option, it seems to me, at least like based on the name and based on the way it occurs in the props. Let's take a look here, state manager additional props. Oh gosh, oh gosh, here we go. So we've got props with optional state props. I mean, public base select props.
04:25 Take a look inside here. You can see how much indirection there is going on here. Library manage attributes, state. Okay, we seem to be on a type of default props here. You can just, is it options for type here? Options, never. I mean, you can see how much indirection there is built into here. IsMulti seems to be pretty clear,
04:45 which is like basically if you specify multi, false or true, and it defaults to false, because if it doesn't default to false, if it defaults to Boolean, let's say, then it can't actually resolve whether it's a single value or a multi value. So we can pause that there. And this group, I actually genuinely don't know how it works,
05:02 but by copying the part of the react select function here, we know that at least these props will be inferred correctly. We can now add new attributes to them. So we can say class name string, for instance, if I'm probably sure it took one already, but like do something as a Boolean.
05:21 And it means that this is now properly extendable, and it means we've built an abstraction that we can let others extend here. So a big lesson here in that you don't necessarily always need to know how the library's types operate in a deeper level. You can just take a little look and understand the type arguments and make sure the type arguments
05:40 are being constrained correctly. Because if we remove the constraints on, let's say group base, for instance, then this is going to just sort of fall apart a little bit. So equals group base option. So if we do that, then it's not going to work. If we say that isMulti doesn't extend a Boolean, then it stops working in various ways.
05:58 So just by copying the API of its type arguments, you can actually get pretty far and build something that works at a really, really decent level.