- One place where type narrowing is incredibly useful
is when you're dealing with function overloads,
and a function overload is essentially
when you have multiple different function signatures
for calling a single function.
This is really common in, like, Node.js,
you'll see this all the time, but oftentimes,
I don't find myself writing overload functions very often
and you probably don't either,
but it is something you'll probably run into,
at least in your TypeScript career, a couple of times,
so it's important to understand how it works.
In our case, we have a sum function,
and the sum function works in two different ways.
In the first option,
I can pass it an array of values
and it'll sum up all of those values together
for me, and in the second option,
I will pass it two separate numbers
and it'll sum those two numbers for me.
So I have two different ways that I can call this function.
You may think of writing the function similarly to this.
I can have a function, sum, I have an option A and B,
and then it's going to do some code inside here,
and option A we know can either be
an array or it can be a number,
so we know that this is going to be a number,
or it's going to be an array of numbers,
and option B we know is optional
and we know it'll be a number.
This may be how you think
about writing this function signature,
but this isn't quite what we expect because, as you can see,
I can actually pass a number to here as well
and that's not how sum should work.
If I pass it just one argument, it's an array,
but if I pass it two arguments, they should both be numbers,
so the fact that I can write
this code right here is incorrect.
So instead, this is where you need
to have a function overload,
where if you want multiple different signatures
for your function,
then you need to define each of them individually
and then write one function that handles all of them.
So what we need to do directly
above this sum function right here,
we need to write our different overloaded versions
of this to narrow down the different ways we can call this,
and I'll leave in this third option here,
this incorrect one,
put it down here.
This should have an error on it
if we've done everything correctly.
So let's do the simplest case first
where we just pass in an array of numbers.
So we're gonna say, function, sum,
this is going to be our numbers,
and it's going to be a number array,
just like that.
By doing this, we've essentially defined our first overload.
We just need to make sure we give it a return type
'cause when you define overloads,
you must give them all return types.
In our case, this will return a number
for what the sum is going to be.
Now, you'll also notice we're still getting an error
and that's because the return type
of this doesn't match the return type of this.
If I just hard code, return the number one here,
you can see it's now working 'cause
this sum function has a number return
and this one also has a number return.
So to recap a little bit how you define an overload,
all you do is you take your normal function implementation
that we should have here and directly above it,
for each overload you have,
you need to define your own function.
It has no implementation.
It just is a function that has
all the parameters and the return type defined,
but no implementation at all, but right now,
we have one implementation of this function done,
which takes in an array.
So as you can see, S1 has no errors.
These other ones have errors
because we only expect one parameter.
So let's write out another, you know,
overload for this where we're gonna have A and B,
which are both going to be numbers, just like that, and now,
if we make sure we type this as returning a number,
you can see that that solves this particular case
where we have multiple parameters as well.
So now, both of these no longer throw
any errors, but you'll notice,
since the only two function signatures we have is
either passing two numbers or passing just a single array,
this option where we pass an array
and a number is throwing an error
because it expects this array to be
a number in order for this to work.
Now, this is great because we've essentially been able
to define the two different ways we
can call our sum function.
This is honestly kind of similar to
a discriminated union, but instead,
we're actually doing a function
that has a few different ways we can handle it.
So instead of a union of types,
we're doing a union where we're saying we
either call this function or this function signature,
even though our actual implementation
is a little bit more generic.
Then, all that's left is to define our function.
What we can do here is just narrow this all down.
So we can say, if, is array, and actually,
I believe it is array.isArray, and we pass an A,
that means that this will return true if A is an array.
So now, if we're inside of here and I console.log A,
you'll see that it has the type of number array,
so we can do whatever code we want.
This would be, like,
some type of reduced function and we
would return the results from that.
I'm not actually going to define this reduced function,
but that's essentially what this would look like,
but for now, I'll just comment this out,
and that's what this code would look like.
Otherwise, if we're in this else scenario,
or we can just come down here, we now know that A,
in our case here, is a number,
and B right now is technically
still either a number or undefined.
Now, you may think, well,
I know for a fact that if A is a number,
then B must also be a number,
but TypeScript's not smart enough to look
at these function signatures when working
on the actual implementation function,
so you need to do a few extra type checks
then you maybe would actually want to.
So here, I would need to be able to check to see
if the type of B is a number,
or I can just make sure that B is not equal
to null, and if that's their case,
I can come in here and I can return A plus B,
and now, as you can see,
everything is working fine.
My function implementation is correct,
I was able to use both these signatures,
and when I'm incorrectly using my signatures,
you can see I'm getting an error thrown,
which is exactly what I want.
Again, this probably isn't something you're gonna use
all that often just because it's a fairly niche use case,
but in the specific scenarios where you do need it,
it's pretty much the only way to solve this type of problem.