Transcript from the "Function Overloading" Lesson
[00:00:29] They're just names that we can use in our code, there's no enforcement of like passing all of this arguments along and you must required parameter here. In TypeScript there is, and so in TypeScript you can have multiple function heads and one implementation that takes care of it all.
[00:00:49] So in this case, we've got the ability to add two number together or add two strings together, and we'll basically do the parsing for you. Along the way, so you provide arrayed x and we will call percent internally. So you may ask like why on earth would I want to do this?
[00:01:12] Well, I'm gonna paste that in so we can play with it because it's a little more useful to once we can see how typescript sees this code.
>> Mike: Okay, so first off, you'll note that I have two function heads here, and then I have a third one, which is a full implementation.
[00:01:42] If I call add you know that I have only two options to pick from, so that bottom implementation although it is most flexible if you look at the shape there we talk about union types, right? It almost like it's the union of the function signatures above, right? It's almost like this shape here is what you would get if you had to accommodate either of these two.
[00:02:08] That's not an accident, right, this is flexible enough to accommodate either of these two, but when I'm actually using it, I don't get access to that very flexible implementation there. I just get the signatures that are above what that means is unlike this signature here it seems to indicate I cannot do this.
>> Mike: Sorry I can do that, I cannot do this.
>> Mike: So, x and y, it would seem, right, that does match this shape, right? I'll even pass the radix in so, take the default parameter out of the picture. So, basically, without this, it's saying you're trying to use the two argument thing, right, so I expect number number that's two arguments number number.
[00:03:05] As soon as I add this third argument in it's says, that's three argument variables well now I want this first thing to be a string. So this let's us put additional constraints up here that say look you can use it this one way or the other way or this 3rd way.
[00:03:23] And now secretly I'm going to implement something that can internally handle all those cases and disambiguate between them. But when you consume this you're only allowed to use those specific ways. So this in fact, this is not just accounting for these two shapes. It accounts for shapes like this.
[00:03:43] Right, that shape that I just added, this one, that was not there in the beginning, bu that also would make this happy, right. So here like now it's gonna be fine, that's fine. But if we say nope, you're not allowed to pass me one number in one strain, even though down here it would seem to allow it Now we're back to that.
[00:04:03] So here's how this works. You want to define function heads from most specific to least specific, right, most specific to least specific. Just think of it, like you were to design almost a case switch. Basically imagine that the way this works under the hood is that you're gonna go through and you're gonna basically look like for the first one you find.
[00:04:29] Then you're gonna hit that most specific first, then fall back to a generic one. That is the mental model here. If you follow that model this will not surprise you. In here, let me make sure I'm not skipping ahead of my slides.
>> Mike: Okay, so effectively, I've shown you what I was about to animate there.
[00:04:53] I do want to show you one other thing now. We can do something like this
>> Mike: So is anyone surprised by this? Versus
>> Mike: So this is the type of x over here string or number obviously. Just string. Just number. So type of has this ability to work with whatever the type of an object is, and sort of help you filter through.
[00:05:35] In fact, if we did something like this
>> Mike: Although this may make other things happy. Now it's gonna say number or object. It's almost like, you handled the string case above and I'm in an else. And therefore in this branch of the code You could only be one of these two types.
[00:05:54] This is really nice when working with unit types. Cuz ultimately, particularly in a case like this, where yeah, we have multiple function heads, but inside this function here, were we not basically passing stringifying potential integers and passing them into parseInt, we found one single code path that will work for everything.
[00:06:13] But were we not doing this, we could absolutely have done some branching. So I can re-implement this if we like and say, let's see if it's smart enough to do this, I've never tried it before. So if typeof x is a string,
>> Mike: We can do something like this.
[00:06:37] We don't need to wrap that in a string anymore even though parseInt wants, it wants a string absolutely wants a string. We're giving it one. That's the only thing it can be. Yeah, it's not quite smart enough to know that our function heads only allow for the case where they are both strings or both numbers.
[00:06:58] So, but no worry, we can just do something like this
>> Mike: And that effectively, like, within this block, we've basically cast, we've implicitly cast Y to a string does that make sense? Without this, we've left open the possibility that y is a number. So we're just working within the context of this function here.
[00:07:23] It's not taking into account that we have these heads. So if we had one number and one string, like parseInt could be unhappy here, but now we're saying, all right, we're asserting both of those.
>> Speaker 2: The act of asking causes that to happen.
>> Mike: It knows it's in a branch of code that can only be run in the event that Y is a string, and so Y is now a string within that plot.
[00:07:48] Pretty cool, right? [LAUGH] And now, down here X can still be a string or number. Y can still be a string or number. So watch this, else if, when we do something like this,
>> Mike: And will return x plus y. See if that works. Nope. It's not going to want that.
[00:08:17] [SOUND] We need to return here.
>> Mike: So we'll just leave it like That, no, how would we fix that,
>> Mike: So that will work, this will work.
>> Speaker 2: So do you even need the f on line time, because isn't that if technically taking cared by the top two decoration things.
>> Mike: If I did not let you look at those, so here's the rule, like we have to design this function according to the constraints of the arguments passed in. So here's how we could do it there's another way we could do this. We could use generics. So what we need, what you're asking it for here is a type constraint where x and y are the same thing, correct?
[00:09:21] So I can make this work, absolutely. Let's make it work. And here's how we would do it. We'd say the function, add, It has a template parameter and we need this template parameter and we could actually say, it is t where t is, all right this'll work. So, we would now say, this is a type T.
[00:09:52] This is of type T as well. And at this point, [SOUND] See, now it's going to say that. T
>> Mike: And then now down here. It doesn't like that.
>> Mike: So we can be assured that now x and y have the same type.
>> Mike: Yeah it would be hard to do that.
[00:10:33] So the reason that we're doing this here essentially is that like yes we can be assured without this other else here. We can be assured that we are in a situation where
>> Mike: Where either x or y or both is not a type string, but that is because it's only taking this into account up here.
[00:10:56] It does not take into account the function heads that are used to ultimately call this thing. But the point is, there is gonna be some branching in here unlike now you can control different call signatures, and then this one that's gonna make you feel uncomfortable, the implementation where it is an overly loose, broad set of constraints, you'll have to write a function that works in that set of constraints.
[00:11:23] However it'll be that is not actually available for other code to consume. That is just designed to handle things that will come in through the much more constrained function heads that you design. This is known as function overloading.
>> Mike: So we could do like, we'd have to do something like this.
[00:11:53] So if x and y are numbers why is it complaining at me now? See else, I'd have to do that.
>> Mike: And then up here.
>> Mike: That'll work.
>> Mike: Does everyone understand how that works? You ready to play with it? [LAUGH] Okay. So. One more thing, sorry. You could not do something like this.
>> Mike: Or so in this situation, there is no way to disambiguate between those. So you have to, when you're picking the multiple function heads, like how on earth would you know which of these you were trying to invoke?
>> Speaker 2: The one that returns, if you cast a variable that you're defining it to as a string
>> Mike: Yeah. You could, let's see, could we do it? My lord, there you go. It could return a number or string. All right, I guess you can't do that, I don't know why you would. Let me express it as advice them rather than a constraint.
>> Mike: Make these distinct and you should treat these almost like, and the type system is a constraint system right?
[00:13:40] We're saying you may only set values of this particular type. You may only point this variable to values of this particular type. This is another constraint in this type system saying yes, I have this sort of broad function here, but you may only invoke it in these specific ways.
[00:13:58] And you must pass three variables, which the first two are strings and the last one is a number, or two numbers, and no other combination will work, despite what you read into here where you can mix and match.