1 00:00:00,210 --> 00:00:02,910 - One place where type narrowing is incredibly useful 2 00:00:02,910 --> 00:00:05,443 is when you're dealing with function overloads, 3 00:00:05,443 --> 00:00:06,990 and a function overload is essentially 4 00:00:06,990 --> 00:00:09,990 when you have multiple different function signatures 5 00:00:09,990 --> 00:00:12,030 for calling a single function. 6 00:00:12,030 --> 00:00:14,160 This is really common in, like, Node.js, 7 00:00:14,160 --> 00:00:16,290 you'll see this all the time, but oftentimes, 8 00:00:16,290 --> 00:00:18,930 I don't find myself writing overload functions very often 9 00:00:18,930 --> 00:00:20,460 and you probably don't either, 10 00:00:20,460 --> 00:00:22,290 but it is something you'll probably run into, 11 00:00:22,290 --> 00:00:24,540 at least in your TypeScript career, a couple of times, 12 00:00:24,540 --> 00:00:26,910 so it's important to understand how it works. 13 00:00:26,910 --> 00:00:28,830 In our case, we have a sum function, 14 00:00:28,830 --> 00:00:31,500 and the sum function works in two different ways. 15 00:00:31,500 --> 00:00:32,370 In the first option, 16 00:00:32,370 --> 00:00:34,230 I can pass it an array of values 17 00:00:34,230 --> 00:00:36,510 and it'll sum up all of those values together 18 00:00:36,510 --> 00:00:38,670 for me, and in the second option, 19 00:00:38,670 --> 00:00:40,710 I will pass it two separate numbers 20 00:00:40,710 --> 00:00:43,020 and it'll sum those two numbers for me. 21 00:00:43,020 --> 00:00:46,290 So I have two different ways that I can call this function. 22 00:00:46,290 --> 00:00:49,410 You may think of writing the function similarly to this. 23 00:00:49,410 --> 00:00:52,980 I can have a function, sum, I have an option A and B, 24 00:00:52,980 --> 00:00:55,020 and then it's going to do some code inside here, 25 00:00:55,020 --> 00:00:57,240 and option A we know can either be 26 00:00:57,240 --> 00:00:59,490 an array or it can be a number, 27 00:00:59,490 --> 00:01:01,380 so we know that this is going to be a number, 28 00:01:01,380 --> 00:01:04,110 or it's going to be an array of numbers, 29 00:01:04,110 --> 00:01:06,090 and option B we know is optional 30 00:01:06,090 --> 00:01:08,070 and we know it'll be a number. 31 00:01:08,070 --> 00:01:09,420 This may be how you think 32 00:01:09,420 --> 00:01:11,580 about writing this function signature, 33 00:01:11,580 --> 00:01:14,280 but this isn't quite what we expect because, as you can see, 34 00:01:14,280 --> 00:01:16,950 I can actually pass a number to here as well 35 00:01:16,950 --> 00:01:18,750 and that's not how sum should work. 36 00:01:18,750 --> 00:01:21,780 If I pass it just one argument, it's an array, 37 00:01:21,780 --> 00:01:25,140 but if I pass it two arguments, they should both be numbers, 38 00:01:25,140 --> 00:01:26,550 so the fact that I can write 39 00:01:26,550 --> 00:01:29,310 this code right here is incorrect. 40 00:01:29,310 --> 00:01:30,600 So instead, this is where you need 41 00:01:30,600 --> 00:01:32,130 to have a function overload, 42 00:01:32,130 --> 00:01:34,170 where if you want multiple different signatures 43 00:01:34,170 --> 00:01:35,280 for your function, 44 00:01:35,280 --> 00:01:37,650 then you need to define each of them individually 45 00:01:37,650 --> 00:01:40,470 and then write one function that handles all of them. 46 00:01:40,470 --> 00:01:41,730 So what we need to do directly 47 00:01:41,730 --> 00:01:44,100 above this sum function right here, 48 00:01:44,100 --> 00:01:46,590 we need to write our different overloaded versions 49 00:01:46,590 --> 00:01:49,500 of this to narrow down the different ways we can call this, 50 00:01:49,500 --> 00:01:51,600 and I'll leave in this third option here, 51 00:01:51,600 --> 00:01:53,370 this incorrect one, 52 00:01:53,370 --> 00:01:54,203 put it down here. 53 00:01:54,203 --> 00:01:55,620 This should have an error on it 54 00:01:55,620 --> 00:01:57,870 if we've done everything correctly. 55 00:01:57,870 --> 00:01:59,610 So let's do the simplest case first 56 00:01:59,610 --> 00:02:01,350 where we just pass in an array of numbers. 57 00:02:01,350 --> 00:02:03,030 So we're gonna say, function, sum, 58 00:02:03,030 --> 00:02:04,590 this is going to be our numbers, 59 00:02:04,590 --> 00:02:06,540 and it's going to be a number array, 60 00:02:06,540 --> 00:02:07,650 just like that. 61 00:02:07,650 --> 00:02:11,070 By doing this, we've essentially defined our first overload. 62 00:02:11,070 --> 00:02:13,260 We just need to make sure we give it a return type 63 00:02:13,260 --> 00:02:14,640 'cause when you define overloads, 64 00:02:14,640 --> 00:02:16,620 you must give them all return types. 65 00:02:16,620 --> 00:02:18,180 In our case, this will return a number 66 00:02:18,180 --> 00:02:20,040 for what the sum is going to be. 67 00:02:20,040 --> 00:02:22,020 Now, you'll also notice we're still getting an error 68 00:02:22,020 --> 00:02:23,340 and that's because the return type 69 00:02:23,340 --> 00:02:25,470 of this doesn't match the return type of this. 70 00:02:25,470 --> 00:02:27,510 If I just hard code, return the number one here, 71 00:02:27,510 --> 00:02:28,740 you can see it's now working 'cause 72 00:02:28,740 --> 00:02:30,750 this sum function has a number return 73 00:02:30,750 --> 00:02:33,180 and this one also has a number return. 74 00:02:33,180 --> 00:02:35,790 So to recap a little bit how you define an overload, 75 00:02:35,790 --> 00:02:38,040 all you do is you take your normal function implementation 76 00:02:38,040 --> 00:02:40,260 that we should have here and directly above it, 77 00:02:40,260 --> 00:02:41,760 for each overload you have, 78 00:02:41,760 --> 00:02:43,320 you need to define your own function. 79 00:02:43,320 --> 00:02:44,790 It has no implementation. 80 00:02:44,790 --> 00:02:46,380 It just is a function that has 81 00:02:46,380 --> 00:02:48,360 all the parameters and the return type defined, 82 00:02:48,360 --> 00:02:50,610 but no implementation at all, but right now, 83 00:02:50,610 --> 00:02:52,890 we have one implementation of this function done, 84 00:02:52,890 --> 00:02:54,000 which takes in an array. 85 00:02:54,000 --> 00:02:56,490 So as you can see, S1 has no errors. 86 00:02:56,490 --> 00:02:57,600 These other ones have errors 87 00:02:57,600 --> 00:02:59,820 because we only expect one parameter. 88 00:02:59,820 --> 00:03:01,650 So let's write out another, you know, 89 00:03:01,650 --> 00:03:04,080 overload for this where we're gonna have A and B, 90 00:03:04,080 --> 00:03:09,080 which are both going to be numbers, just like that, and now, 91 00:03:09,240 --> 00:03:11,970 if we make sure we type this as returning a number, 92 00:03:11,970 --> 00:03:14,400 you can see that that solves this particular case 93 00:03:14,400 --> 00:03:16,590 where we have multiple parameters as well. 94 00:03:16,590 --> 00:03:18,450 So now, both of these no longer throw 95 00:03:18,450 --> 00:03:20,250 any errors, but you'll notice, 96 00:03:20,250 --> 00:03:22,740 since the only two function signatures we have is 97 00:03:22,740 --> 00:03:26,040 either passing two numbers or passing just a single array, 98 00:03:26,040 --> 00:03:27,540 this option where we pass an array 99 00:03:27,540 --> 00:03:28,890 and a number is throwing an error 100 00:03:28,890 --> 00:03:30,570 because it expects this array to be 101 00:03:30,570 --> 00:03:32,760 a number in order for this to work. 102 00:03:32,760 --> 00:03:35,280 Now, this is great because we've essentially been able 103 00:03:35,280 --> 00:03:36,900 to define the two different ways we 104 00:03:36,900 --> 00:03:38,040 can call our sum function. 105 00:03:38,040 --> 00:03:39,630 This is honestly kind of similar to 106 00:03:39,630 --> 00:03:41,550 a discriminated union, but instead, 107 00:03:41,550 --> 00:03:42,900 we're actually doing a function 108 00:03:42,900 --> 00:03:45,180 that has a few different ways we can handle it. 109 00:03:45,180 --> 00:03:47,040 So instead of a union of types, 110 00:03:47,040 --> 00:03:48,390 we're doing a union where we're saying we 111 00:03:48,390 --> 00:03:50,970 either call this function or this function signature, 112 00:03:50,970 --> 00:03:52,650 even though our actual implementation 113 00:03:52,650 --> 00:03:54,210 is a little bit more generic. 114 00:03:54,210 --> 00:03:56,790 Then, all that's left is to define our function. 115 00:03:56,790 --> 00:03:59,160 What we can do here is just narrow this all down. 116 00:03:59,160 --> 00:04:01,650 So we can say, if, is array, and actually, 117 00:04:01,650 --> 00:04:05,070 I believe it is array.isArray, and we pass an A, 118 00:04:05,070 --> 00:04:07,380 that means that this will return true if A is an array. 119 00:04:07,380 --> 00:04:11,340 So now, if we're inside of here and I console.log A, 120 00:04:11,340 --> 00:04:13,710 you'll see that it has the type of number array, 121 00:04:13,710 --> 00:04:15,090 so we can do whatever code we want. 122 00:04:15,090 --> 00:04:15,923 This would be, like, 123 00:04:15,923 --> 00:04:17,760 some type of reduced function and we 124 00:04:17,760 --> 00:04:19,050 would return the results from that. 125 00:04:19,050 --> 00:04:21,090 I'm not actually going to define this reduced function, 126 00:04:21,090 --> 00:04:23,190 but that's essentially what this would look like, 127 00:04:23,190 --> 00:04:24,930 but for now, I'll just comment this out, 128 00:04:24,930 --> 00:04:27,060 and that's what this code would look like. 129 00:04:27,060 --> 00:04:29,040 Otherwise, if we're in this else scenario, 130 00:04:29,040 --> 00:04:31,230 or we can just come down here, we now know that A, 131 00:04:31,230 --> 00:04:33,210 in our case here, is a number, 132 00:04:33,210 --> 00:04:34,890 and B right now is technically 133 00:04:34,890 --> 00:04:37,530 still either a number or undefined. 134 00:04:37,530 --> 00:04:38,730 Now, you may think, well, 135 00:04:38,730 --> 00:04:40,950 I know for a fact that if A is a number, 136 00:04:40,950 --> 00:04:42,600 then B must also be a number, 137 00:04:42,600 --> 00:04:44,460 but TypeScript's not smart enough to look 138 00:04:44,460 --> 00:04:46,830 at these function signatures when working 139 00:04:46,830 --> 00:04:48,870 on the actual implementation function, 140 00:04:48,870 --> 00:04:50,940 so you need to do a few extra type checks 141 00:04:50,940 --> 00:04:52,770 then you maybe would actually want to. 142 00:04:52,770 --> 00:04:54,600 So here, I would need to be able to check to see 143 00:04:54,600 --> 00:04:56,550 if the type of B is a number, 144 00:04:56,550 --> 00:04:58,530 or I can just make sure that B is not equal 145 00:04:58,530 --> 00:05:00,450 to null, and if that's their case, 146 00:05:00,450 --> 00:05:03,840 I can come in here and I can return A plus B, 147 00:05:03,840 --> 00:05:04,980 and now, as you can see, 148 00:05:04,980 --> 00:05:06,420 everything is working fine. 149 00:05:06,420 --> 00:05:08,250 My function implementation is correct, 150 00:05:08,250 --> 00:05:09,990 I was able to use both these signatures, 151 00:05:09,990 --> 00:05:12,180 and when I'm incorrectly using my signatures, 152 00:05:12,180 --> 00:05:13,830 you can see I'm getting an error thrown, 153 00:05:13,830 --> 00:05:15,540 which is exactly what I want. 154 00:05:15,540 --> 00:05:17,580 Again, this probably isn't something you're gonna use 155 00:05:17,580 --> 00:05:20,580 all that often just because it's a fairly niche use case, 156 00:05:20,580 --> 00:05:22,740 but in the specific scenarios where you do need it, 157 00:05:22,740 --> 00:05:25,740 it's pretty much the only way to solve this type of problem.