Going from Function Comparisons to Object Comparisons
The reason why the
@ts-expect-errors aren't working as expected is that TypeScript doesn't really care about excess properties you pass to objects.
Let's look at an example to illustrate.
How TypeScript Handles Excess Properties
const tagState of type
0:01 The reason this is happening is that TypeScript doesn't really care about excess properties you pass to objects. Let me explain what I mean by that. Let's imagine that we have this const tagState here which is TagState. This is an object. Let's say we pass in tagSelector. Let's say it's 1, or an actual 1, rather. Then we've got tags, which is just an empty array.
0:26 This is satisfying this criteria. If I try to pass in anything that doesn't satisfy this -- let's imagine we have a random function inside here -- then it's going to say, "Object literal may," blah, blah, blah, blah. It doesn't exist. In this situation, TypeScript is saying you can't pass excess properties to that point.
0:48 Let's imagine that we have a function called getTagState, which is a function that returns TagState. I need to just change a couple of things here. This is going to return this. This is a little bit hard to look at. Let me just change this into a type itself. GetTagState is this function. Then I'll add it here.
1:10 In this situation, you are actually allowed to add excess properties. TypeScript won't yell at you. Why is that? Because in the previous version, TypeScript was checking if the object matched another object. Whereas here, we're checking if this function matches this function.
1:31 In TypeScript, you are allowed to pass excess properties from the return values inside functions. This is important because it means that you...There was lots of discussion when the TypeScript team came up with this idea. It's in different languages. It's called...It's either covariance or contravariance. I can't remember which one it is.
1:53 Anyway, it's one of those concepts which allows you to pass excess properties in these situations, because it means that these functions can be a little bit more lenient. What this means then is that when we pass a function to our setState call here, we're allowed to return excess properties within that setState.
2:15 This function here, it's being a little bit more loose than you expect it to be. The way that you can get around this is, actually, if we type this function itself...Internally, we say inside the function, this is a function that returns TagState.
2:33 Now what it's doing is it's actually doing an object comparison again. It's comparing the return type from within the function and saying, "OK. This one now returns an excess property, so I'm going to throw the excess property error." So, "May only specify known properties."
2:50 This means then that inside our functions here, we could say, "TagState." Now we're going to get an error inside tagselected and inside here too. This one returns TagState. Again, we get the error deep inside this array value there.
3:08 This is not necessarily a good solution though. It's just something to be aware of when you're using useState and when you're using especially these functions. It's interesting that just a little API like this can give you an insight into how TypeScript thinks about function comparisons and object comparisons.
3:25 In general, not only in React but in the wider TypeScript world, you should be trying to make TypeScript do object comparisons, because you're going to get better errors, rather than function ones.