The Weird Parts of TypeScript 13 exercises
solution

Understanding Unexpected Properties in TypeScript

There are a couple of ways to solve this challenge.

Solution 1: Annotate the Mapping Function

The first way to solve this issue is to annotate the map function.

In this case, the mapping function will take in a user that is an object with a name string, and an index which will be a numbe

Loading solution

Transcript

00:00 Okay, so the reason this is happening is that users.map is returning basically an array of id, age, and name. We can see that in the return type, which is just here. So id, age, and name are being returned. And because of that, it's not actually going to error inside this function.

00:19 If we think about this function and we pull it out, and we say const mapping function here like this, and we say user, sure we need a name string and the index is going to be a number, and then we pass that back to users.map. So now we've got the same code as before, but the mapping function is now outside of the map instead of inline.

00:38 Then, why would we get an error here if we're using users.map in this setup? Because if you think about it, sure, like the mapping function returns an extra property, but TypeScript doesn't really mind about that extra property. It's because, just because it's got an extra property, it's not going to break anything at runtime.

00:57 So users.map this mapping function then. How do we inside this mapping function say, actually no, we just want to return id and name? Well, the first way we could do it is actually add a return type annotation onto the mapping function and say this must return a user.

01:16 So now, because TypeScript is basically, now it's like when you return something from this, it's going to compare it to the thing that's in the return type. So now it's going to error on age. But if we slightly mash this up a little bit and we say, let's say we return a user,

01:36 and now we say, oh no, modified user, and now we say const modified user is this, it's no longer going to error because it's not in the exact position it needs to be. So it's not performing excess property checks.

01:53 So again, the way to handle this would be to say modified user is a user, and now age is going to error at us again. You can see how this catches you out again and again. Another way to do this would be if we return to this setup with it inlined, is to say that this object satisfies user in this position.

02:10 Now age is going to be erroring at us properly because you can't pass property age to type user. So it's just really nasty the way that excess property checks just sometimes catch you out and they occur in situations that you don't expect,

02:27 or rather they don't occur in situations that you don't expect. So again, make sure that when you do have these situations where you're adding excess properties, that when you're declaring these variables, you're giving them types. So you're saying modified user, it is a user and it has an age of 30. Or if you're inlining it, let me see if I can inline this variable there.

02:46 Lovely. Then you're declaring a return type on your functions. And by the way, we could inline this mapping function if we wanted to. I can just remove these annotations. There we go. So now when we basically say users.map, we add the return type just here, just after user and index.

03:05 And now we're getting the proper error on the excess property. So again, excess property warnings, they can catch you out in all sorts of various ways, especially when it comes to function returns.