Unions and Narrowing 28 exercises

Narrowing Behavior Across Scopes

Narrowing doesn't always carry over into other scopes in TypeScript.

In order to understand what's happening here, let's refactor the code a bit so the filter function looks for the includes(searchParams.name) method on user.name:

const findUsersByName = (
searchParams: { na

Loading solution


00:00 Okie doke. The reason this is happening is because narrowing doesn't always carry over into other scopes in TypeScript. Let's map this out a little bit cleaner. Let's actually say users.filter and return user.name.include searchparams.name.

00:16 Now you can see here that there are several different scopes happening. There is the main function body scope inside find users by name. Then we say if searchparams.name and we open up a new scope here. Then we have users.filter and inside the users.filter we're passing another function which has its own function body.

00:36 So we have three separate scopes. The outer function body scope and of course there's even beyond that the module scope too. Then we have the if statement scope. Then we have this scope inside here inside the filter function.

00:52 So what's happening here is that TypeScript won't always carry over kind of like function or like narrowing into different scopes. So we have here if searchparams.name and searchparams.name you'll notice that it's mutable.

01:10 We can if we want to change searchparams.name to equal undefined and we can put this code in at any time. We could even put it in here if we wanted to.

01:20 And what this means is that if we were to let's say I don't know let's say we say users and we select the first user and we say yeah we'll do this I think. Now we say users first one.name includes searchparams.name. We're now outside of the filtering body.

01:40 If we do it here there's no error because searchparams.name is inferred to be string in this particular scope. How weird is that? Whereas if we go back into the other scope then inside here TypeScript doesn't really know or can't really tell if this one should be string or undefined because when this function

01:59 is declared it could be called multiple different times and the thing that it's referencing is mutable. So in the production of this function body or for instance if it gets called later or something like that TypeScript doesn't know how this function is going to be used. This could be like a subscribe function which is called at some later date.

02:16 TypeScript doesn't know that filter is just called synchronously with all of the members here. So because of that because TypeScript doesn't know all of that information it removes the narrowing that was done on searchparams.name. Now I just want to check something out quickly here. No a read-only annotation doesn't work

02:34 but something that will work what we can do is we can say let's just say we say let searchparams.name equals searchparams.name here and now instead of referencing searchparams.name we stick it in here. This is might be something that you sort of land on you think okay maybe

02:51 this will work. Okay now searchparams.name is its own variable and it's a let now but the let has the same issues as the object did. It could be changed at any time. Anytime we want to we could

03:04 go in and change searchparams.name. What if we though change it to a const? Oh okay now we've changed it to a const we know that searchparams.name can never change so when we narrow it inside here

03:17 we now know that the thing that's been narrowed is a const and can never change and so even inside this if body here this scope then anything inside that scope is guaranteed to have that be a string. Really nice because that has been saved in a variable that's a const that can never be changed

03:37 at runtime. So this is a really really important thing to grok because it's it's so fundamental to the way that TypeScript thinks about narrowing. Any of the narrowing examples we've seen so far can be disrupted by sort of putting them in different scopes whereas if you save things

03:53 into const variables like this then it means that TypeScript can be sure and also your code can be sure too. That really that thing can never be changed and never be mutated and never in this case be undefined.