The Utils Folder 10 exercises
solution

Enable Generic Functions to Better Infer Types

The first step is to add a type parameter onto uniqueArray. This turns uniqueArray into a generic function that can receive type arguments:

const uniqueArray = <T>(arr: any[]) => {
  return Array.from(new Set(arr));
};

Now when we hover over a call to uniqueArray, we can see that

Loading solution

Transcript

00:00 Okay, the solution here is devilishly simple. We're gonna add a type parameter onto unique array. This turns unique array into a generic function, one that can receive type arguments. And now on this T, if we take a look at unique array here, it's being inferred as unknown. Now, why is that?

00:19 Well, we haven't passed any type arguments to this. So of course, if there's nothing here and there's no default, it defaults to unknown. Okay, well, how can we make it so that, because we want unique array here to infer a number, right? Because we know that the thing we're getting back is an array of numbers. So what we can do is on here,

00:38 we can say the return type of this is going to be an array of T. So now we can look at results and it's an unknown array. Very nice, because it's inferring nothing essentially. So it's basically saying, right, you've passed no type argument. We're gonna put this as unknown. And then we'll return an array of unknown there too.

00:57 Now, how do we get it so that, because sure, we could say, right, let's just pass a number into here. Very nice. And now result is an array of numbers. But there's actually no relationship between the things that we're passing in and the thing that we're getting out. So we could just say this is an array of strings here. Brilliant.

01:17 But actually like this, the array in the function is actually still typed as any array. How do we get it to infer the type of number from the thing that's passed in? Well, we can say this T is actually the thing that gets passed in. It's an array of T here.

01:35 So we've got this result being a number array. Fantastic. So now unique array, you can see that because we're passing an array of numbers, we get back or gets inferred in the type argument as a number. If we were to add a type argument in here manually, you would see that this would work too.

01:54 So of course, now we get to say, okay, we're passing an array of numbers and we don't need to actually do any inference on TypeScript. It's just saying, oh, sure. You passed in number, you get a number. This means that if we add in, let's say a Boolean into here, it's going to error because it's not assignable to the type argument we passed in.

02:13 But what happens if we remove the type argument? If we say, okay, just remove unique array. Now we would get number or Boolean in the slot. Isn't that interesting? So what happens is if you pass a type argument, TypeScript will say, I see, you forced unique array to be,

02:32 so you've passed it a type argument manually. If you don't pass a type argument, then it will attempt to infer it from the runtime arguments that you pass it in. So here we're saying number or Boolean because we've passed in both. And if we don't pass in a Boolean, then what you end up with is a number array.

02:50 And down the bottom, we've got an array of strings too. This is extremely, extremely powerful. And note too, that we don't actually need to annotate the return type here. TypeScript understands that, sure, you've created a new set of T, and then you're creating an array from T. So it understands that the actual thing you're getting back

03:09 is an array of T. So, brilliant. We just have number array and string array, and it just works. This is the source of so much powerful TypeScript out in the world because, sure, you've got your functions here, and this type signature is relatively complex for new people to understand.

03:28 But when you use it, you don't need to pass in any type arguments. And const result equals unique array just looks like JavaScript, except it has superpowers.