Pass Type Arguments to a Function
Before we start the solution, let's look at the errors we get from
const stringSet = createSet<string>();const numberSet = createSet<number>();
Hovering over each of these, we see the following error message:
Expected 0 type arguments, but g
0:00 The solution here is to first of all, add a generic onto createSet. The error that we're getting here is "Expected type arguments, but got 1." This here is a type argument that's being passed to a function. We can grab the T here.
0:16 This satisfies this area here. We now know that createSet will manually pass in the type argument there of string to it. What we need to do is make sure that the Set that's being returned, is of the right type. What we can do is we can actually pass in the type into the Set here.
0:35 This Set that we Command-click on to it. Then it's actually an interface that's implemented globally and you can see that it has a T on it. It has a type argument that you can pass it. This is a type helper basically, but it's also being implemented in the global scope as a class.
0:52 We have various things like add and delete, etc., that all come from this T inference here. Usually, when you create a new set, let's say we have set = new Set, then we will be able to pass it like a string. Then you'll be able to say set.add(124123).
1:11 Then this would be an error and it certainly will be when my TS server catches up to me. You can see "Argument of type 'number' is not assignable to parameter of type 'string'." Whereas if we change this to number, then it's going to work. What if we don't pass it anything?
1:24 Well, TypeScript will try to infer what's happening here. If it can't infer it, then what it's going to do is it's basically just going to say Set<unknown>. Unknown is like the default argument type for all of this TypeScript stuff.
1:41 We have set.add. This means that we can pass anything into here, right? Which is probably not desirable, but this is a really interesting problem because we have a generic slot here that's not actually inside the arguments.
1:55 If we added initialValue, for instance, and this had to be T, then we wouldn't need to pass in a type argument here because we could just go like blah, blah, blah. Then this would be blah, blah, blah. Then we would have our number inferred and also our string inferred too.
2:15 This use case when you don't have an initial value or maybe the initial value is not required, then this means that you end up with a cool way of adding these type arguments in yourself, which is this syntax here.
2:31 You'll find this come up with a lot of different use cases that you might want to implement, but also in like the built-in types with Set and Map and all of these different things. They often require you to pass in a type argument, even if they don't say that they require you to in order to make them type save.