I'm struggeling with the control flow and a Signal Input in Angular 17.2.
I have this input in a component:
index = input<number|null>();
And I have this template. Since index can also be 0 I need to explcitly check for null and undefined:
@if (index() !== null && index() !== undefined) {
<div appMyComponent [index]="index()!"></div>
}
Now I need ! to make it work since index() can be undefined or nullagain, at least for the compiler.
I can't use @if (index() !== null && index() !== undefined; as index) { } since index would be a boolean.
How to solve this correctly?
Lets start by understanding why when you do:
You have to use
!.Your input is defined as such:
In order to consume an input, you have to call it, because it is a function. It's exactly what you've done.
Do you always get a similar result when you call an impure function? No, it can be different. Hence why the compiler won't consider your 2 checks enough to then consider that the
indexyou're passing as an input in your@ifis not null.It's simple to reproduce this in Typescript and see what the compiler tells us:
We clearly see here that the
valueis eithernumber,nulland can even be undefined (this is due to the fact that it's not a required input, so if it's not set at all, the value will be undefined).Now, how do deal with this?
Let think about that solution in Typescript first and see how we can transpose that within the context of an angular template.
When calling a function can give you a
number | null | undefined, if you want to make checks on the value and have your compiler know that from this point, you've got something that isn'tnullnorundefinedand that therefore it's a number: Save it in a variable, and make the checks on that variable. Because you're not calling the function again which could give a different result, the compiler is then able to infer that the type isnumberafter the checks:Final step: How to transpose that solution to an Angular template?
Well, there's a little trick that it was possible to use the old syntax too (
*ngIf). I wrote a blog post about it 4.5 years ago.Here's the bottom line of it. The following is totally valid:
What's interesting about it, is that the
*ngIfwill always be true, as what it receives is an object. So you can store observable values into variables, wrap integers so that*ngIfdoesn't consider the0as falsy, etc.With the new
@ifsyntax, it works the same. And here's in your case how you can achieve what you want:Now that we call the
indexfunction only once, store it in a variable, make an assertion on that variable, Typescript is able to narrow down the type accordingly.I have made a Stackblitz repro for you to play with where you'll be able to see the 2 experiments, one test with your current setup and the other with the one I just shared. Your code has a compiler error, the one I shared works fine: https://stackblitz.com/edit/stackblitz-starters-wfvrva?file=src%2Fmain.ts