React and TypeScript Function Overloads Solution
This course has been updated! We now recommend you take the React and TypeScript, v2 course.
Transcript from the "Function Overloads Solution" Lesson
>> I am back, and we are going to add some types to this terrible TypeScript function cuz right now it just says with any all over the place, which is why even have this in TypeScript? All right, so let's think about this, like I said before, this case here has to be able to support, any other possible conditions.
[00:00:17] And you can see that like passing that one number, is already upsetting TypeScript. So first thing we know that we need to do is, we need to say like that b can be optional, right? That that appeased it but it still doesn't really kinda we still have any all over the place, it's tricky.
[00:00:37] We know that one of the cases is that it returns a number, Which now upsets it when we try to return a function. Before it was, I'll take anything and I'll return anything, I might as well not even be types here. But now we're saying, one of the cases is that it could return a number, and we know that the other case is that it can return a function, that takes a number and returns a number.
[00:00:58] And you can do this in line, and if it makes you sad, you can pull it out. So we can say really if you didn't have a b, let's take another may have a function that takes a b argument, which should be a number, and it returns a number, right?
[00:01:20] That that should work in a second right right now it's angry, we had to put in parentheses. We in the messages helps too I don't know that parentheses will work like that, but we can also pull it out as well. And so now this will theoretically do the trick for us, we'll deal with that in a second.
[00:01:39] But we have the situation where the function it'll either has an a, it possibly takes a b, or return a number, or a function that will return a number. Cool, so that'll work for us, but we wanna add those overloads in there as well. So, the two signatures that we want to take two arguments, you get back a number, one argument you get back a function that takes a number, so let's start with the easier one so this a function.
[00:02:10] Again it looks exactly the first line in a function declaration so we'll say in this case a is number, and b is also a number. So now it's no longer optional in this case in that case, we will in fact, return a number. Cool, and that is making everything happy except for the ones when I pass one in there, so let's actually support that use case as well, and you can see those got really angry down there.
[00:02:38] And so in worst case we have one that only supports a number, in this case it will be that, Second piece here, And now TypeScript is happy. If you give back two numbers, it knows it's the version that takes two numbers and returns a number, if you give it one number, it knows it's the version that takes one number and returns a function waiting for the second number, right?
[00:03:02] So we've now given this add function to different function signatures, depending on how it was called. And TypeScript can now confirm and now it's not giving you like yeah, it could be a function or it could be a number or anything along those lines, it now knows kind of deterministically the two that it supports.
[00:03:19] So it can winded down but again this line here has to be kind of the cover all your bases, it will work and then you can basically refine the type above with the overloads. All right, so now let's bring that into a react situation. Right, so we're gonna go into this demanding props based on other props, and we have a version of our text component from before, and it can be expanded and truncated.
[00:03:57] And let's open this app real quick, so there's two props here we'll see in a second. We we wanna say is, that we can only use the expanded boolea, if it's truncated, right? We don't wanna support saying expand is true or false, if it's not truncated, right? Again, this is one of those things where in a component library you just wanna be deterministic, on how your code is expected to be used, right?
[00:04:27] So right now I could totally take out truncate, and expanded as still supported, I wanna make, Truncated or truncate yes or no for that one but like you cannot use expanded unless truncate is included, that is that is the goal of what we are seeking to do in this case.
[00:04:51] All right, so we have to make three combinations of this component. Just every use case which is what we have, right we're already one-third of the way there, we have the one that can handle either truncate or expanded. Now we wanna to have one that is just truncate with no expanded prop whatsoever, and then we want truncate and the optional expanded prop.
[00:05:20] Now what's important there is what we don't have, which is we're not going to write an implementation, that supports having an expanded prop without a truncate prop, right? So expanded should only exist if truncate is true. All right, so how would this look? Well we've got TextProps and TextProps will end up being that pattern that we've used a few times so far which is, what are the common things remember we did this to the buttons.
[00:05:52] We had one with primary was a boolean, but the other two were never we had all the different combinations. But the idea that there was a child react child, their children, was common to all the different iterations that we might have around the type. So we'll keep that, and we'll make a type called NoTruncateTextProps, Which is everything that's in TextProps plus, we're gonna say truncate is false.
[00:06:31] Just to kind of be clear about this, we're not saying that's the boolean, we're saying that this is a version where it is set to false in this case. And then we will have another version called TruncateTextProps, and that is going to be TextProps, in a situation where truncate is true.
[00:06:56] And of truncate is true, then expanded can be a boolean. So what we don't have is a situation where truncate is false and expanded exist on the type at all, right? Or once we move it from here, so this is the children, this is a version that where truncate is false or undefined.
[00:07:18] And this is one where it's true and then we will take an expanded prop, so if truncate doesn't exist, there's no world where we take an expanded prop. Now we need to add those additional signatures, on top of the component, so those will go right here. The other thing you'll notice that like, generally speaking this course I prefer arrow functions just cuz I do but, with overloads I don't believe there is a syntax that supports arrow functions so, you have to do it as a function declaration.
[00:07:56] So here one that is text and that is going to be where the props are the NoTruncate, TextProps, and will return the JSX.Element. We'll have a situation where we do have the TruncateTextProps, And will return a JSX.Element, JSX has a red squiggly line, just cuz code sandbox is es lint, is like I don't know what that is, but it's supported in the react types so don't stress out about that.
[00:08:37] And then finally we'll have the kind of base case here, which we could have common TextProps or something like that as well, I'm just gonna write it in line in this case. Where it's gonna be, We'll say it's, TextProps, so that common one, and then it's basically what we had earlier, right?
[00:09:12] Where we had the two optional ones cuz again every situation that could happen has to be supported. So we could say this point that truncate is optional, Is and is Boolean, And expanded. It's optional and it's a boolean as well, so basically what we had originally. And now you can see that we've got the kind of base case that we had before, plus these two refinements on it.
[00:09:45] So now if we go ahead and we remove truncate, it's angry with us. It's you can't expand something that's not truncated, but we can remove expanded, right? So again, the theme that we've been hammering on here is the ability to be a little bit just more intentional about how you expect this component to be used.
[00:10:06] To hopefully reduce edge cases right again that kind of pseudoscience doesn't feel fair because I'm sure there was some research done into the idea of having a type system reduces bugs by 15% or whatever. You can see where that would come from at least, the idea that we're covering just enough edge cases in how we get sophisticated with our types.
[00:10:29] That a whole bunch of just ways that a component might accidentally be used, are no longer allowed by our type system, right? And so we can do that as well, all right great, question. Yeah, the chat points out that function declarations are also preferred cuz they hoisted better and you can actually use them and define them below.
[00:10:55] Yeah there's a lot of reasons why a lot of people still prefer function declarations, it's a matter of choice. If you use a function expression, you have to define it before you use it. And it used to be in the chrome tools that, you wouldn't get the name of the function and your error messages.
[00:11:14] The developer tools have gotten better where you actually will get the name of the function even if you use a function expression. But they behave slightly differently, this kind of goes back I think we talked about this briefly when we talked about types and interfaces where they're 90% the same, but slightly different.
[00:11:33] And you need to be aware of the fact that they could behave slightly differently but it ultimately does come down to a matter of preference. I'm gonna walk a very fine line here, how about this? I think that function declarations are probably the better choice, but I still write function expressions using arrow functions.