Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type check in @if (and *ngIf) causes compiler errors while linter passes #59215

Open
kisfairmed opened this issue Dec 17, 2024 · 9 comments
Open
Labels
Milestone

Comments

@kisfairmed
Copy link

Which @angular/* package(s) are the source of the bug?

compiler

Is this a regression?

Yes

Description

I am using @if (with the same behavior observed for *ngIf) to check a variable’s type. While the TypeScript linter accepts this type check without issues, the Angular compiler (ng build and ng serve) fails to compile.

However, if I replace the inline type check with a TypeScript type-guarding function, everything works as expected.

I don’t see a clear difference between the two approaches, so I suspect this might be a bug.

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/stackblitz-starters-lk71pysu?file=src%2Fmain.ts&view=editor

Please provide the exception or error you saw

✘ [ERROR] NG2: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type. [plugin angular-compiler]

Please provide the environment you discovered this bug in (run ng version)

Angular CLI: 19.0.5
Node: 22.12.0
Package Manager: pnpm 8.12.1
OS: darwin x64

Angular: 19.0.4
... animations, common, compiler, compiler-cli, core, forms
... localize, platform-browser, platform-browser-dynamic, router

Package                            Version
------------------------------------------------------------
@angular-devkit/architect          0.1900.5
@angular-devkit/build-angular      19.0.5
@angular-devkit/core               19.0.5
@angular-devkit/schematics         19.0.5
@angular/cdk                       19.0.3
@angular/cli                       19.0.5
@angular/material                  19.0.3
@angular/material-moment-adapter   19.0.3
@schematics/angular                19.0.5
rxjs                               6.6.7
typescript                         5.6.3
zone.js                            0.15.0

Anything else?

No response

@crisbeto
Copy link
Member

It looks like we're hitting a bug in TypeScript. The compiler produces the following code to check the template:

if ((typeof (((this).value))) === ("number")) {
  "" + this.value * 2;
}

It seems like the parentheses are throwing off TypeScript's narrowing. This has happened in the past, see microsoft/TypeScript#56030.

Also example from the TS playground: https://www.typescriptlang.org/play/?#code/DYUwLgBAbghsCuIIF4IEYBMBmCMDOEeYATgJYB2A5hAD4TnwC2ARiMQNwBQnA9DxAHUA9sQDWeTqQBmEABRgAngAcQQmbARJk2iAHIGLNroCUEAN6cIEAMZDyeIaAB0wIZVkbEEAFQQMxrgBfbj4IABEhEAJyIUgAdxFRSRlZeWVVdThEU21UfSZWYhNzSxs7B2dXd08kX38gkP4IqPpYiASxZLlFFTVoLK0dWXzDIuNTCytbe0cQFzcPAZ8-AM5g3ibI6LaOpOk5NN7MzRyhkcKTCdLpirmqxc1l+rWgA

@crisbeto
Copy link
Member

I've filed an issue with TypeScript: microsoft/TypeScript#60784

@pkozlowski-opensource pkozlowski-opensource added the area: compiler Issues related to `ngc`, Angular's template compiler label Dec 18, 2024
@ngbot ngbot bot added this to the needsTriage milestone Dec 18, 2024
@JeanMeche JeanMeche marked this as a duplicate of #59366 Jan 5, 2025
@JeanMeche
Copy link
Member

The issue to track is microsoft/TypeScript#42203 (above was closed as duplicate)

@kisfairmed
Copy link
Author

Please correct me if I’m wrong, but while the linked TypeScript issue would solve this problem, it’s the Angular compiler that generates this seemingly incorrect TypeScript code, isn’t it? Therefore, we could fix it. And since it prevents developers from proper type guarding in HTML, I feel like we should.

@crisbeto
Copy link
Member

crisbeto commented Jan 6, 2025

We generate parentheses around expressions to ensure that the comments before them are associated by the TS compiler correctly and we add comments before expressions in order to map errors back to their source location. That being said, I think we should fix it on our end, because the TS issue has been open for a long time and there hasn't been any traction.

We can resolve it by generating the typeof expressions without template mappings, but adding separate code to type check them. The tricky part is that we would have to generate two pieces of type checking code for one expression and our current infrastructure isn't set up for it, as far as I can tell.

@JeanMeche
Copy link
Member

A PR is expected soon on TS's side.

That would fix it when we support 5.7 (in 19.1 and when the fix landed in a TS patch).

@JoostK
Copy link
Member

JoostK commented Jan 7, 2025

PR in microsoft/TypeScript#60928. This is very unlikely to land in a TS 5.7 patch; it's mostly only recent regressions that are fixed in TS patches. So TS 5.8 at the earliest.

I suppose we could address this in the TCB as well, the parenthesis may not be strictly needed (they are at times to ensure proper diagnostic ranges, but that's not necessary in all cases).

@JoostK
Copy link
Member

JoostK commented Jan 7, 2025

We can resolve it by generating the typeof expressions without template mappings, but adding separate code to type check them. The tricky part is that we would have to generate two pieces of type checking code for one expression and our current infrastructure isn't set up for it, as far as I can tell.

We can do this:

if (((typeof x) === ('string') && (typeof x === 'string' /* D:ignore */)))

i.e. emit twice and using markIgnoreDiagnostics on the second occurrence.

@crisbeto
Copy link
Member

crisbeto commented Jan 7, 2025

Fitting this into our current system might be tricky since each typeof foo expression gets processed in isolation. We may potentially have to introduce another op specifically for typeof expressions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants