angular / angular

Deliver web apps with confidence 🚀
https://angular.dev
MIT License
94.98k stars 24.84k forks source link

Using ?. in template does not narrow type in *ngIf directive #47077

Open kbrilla opened 1 year ago

kbrilla commented 1 year ago

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

compiler, core, language-service

Is this a regression?

No

Description

with strict mode on for TypeScript

  "compilerOptions": {
    "strict": true,
    ...
export type AccessibleValue<T> =
  | { isAccessible: true; value: T }
  | {
      isAccessible: false;
      value: null;
    };

evaluates to string correctly in typescript

 if (this.testValue.accessible?.isAccessible) {
      this.stringOnlyToTest = this.testValue.accessible.value; 
    }

but in the template, it throws

Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'.
<p *ngIf="testValue.accessible?.isAccessible">
{{ testValue.accessible.value | stringOnlyPipe }} <span></span> 
</p>

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/angular-ivy-gpknqn?file=src/app/app.component.ts

Please provide the exception or error you saw

Argument of type 'string | null' is not assignable to parameter of type 'string'.
Type 'null' is not assignable to type 'string'.

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

Angular CLI: 14.1.1
Node: 14.16.0
Package Manager: npm 6.14.11
OS: win32 x64

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

Package                         Version
---------------------------------------------------------
@angular-devkit/architect       0.1401.1
@angular-devkit/build-angular   14.1.1
@angular-devkit/core            14.1.1
@angular-devkit/schematics      14.1.1
@schematics/angular             14.1.1
rxjs                            7.5.6
typescript                      4.6.4

Anything else?

I'm pretty sure there are actually more problems with regards to ?. operator in templates and will try to provide as many as I am able to find while fixing the project I work on currently

kbrilla commented 1 year ago

Hey @AndrewKushnir any info on this? I double checked it and type narrowing works as long as there is no optional chaining involved so it is definitely ?. operator in template. Here is example form before but without ? in itnerface and no ?. used example

amakhrov commented 1 year ago

This looks like a shortcoming of Typescript. A type guard function (used internally by *ngIf for type checking) doesn't narrow down a type of an expression with optional chaining:

declare function isNotNull<T>(v: T): v is NonNullable<T>;

declare const nullable: {value: string | undefined} | undefined;
if (nullable?.value != null) {
    nullable.value.toUpperCase() // OK, properly narrowed down to non-nullable
}

if (isNotNull(nullable?.value)) {
    nullable.value.toUpperCase() // ERROR: "nullable" is possibly undefined
}

Playground

https://github.com/microsoft/TypeScript/issues/34974

zetti-caletti commented 1 year ago

Should be fixed soon. It's really annoying.

twittwer commented 5 months ago

The same problem exists in the new control flow @if

https://stackblitz.com/edit/stackblitz-starters-ygefyd

JoostK commented 5 months ago

Related: #52855

amakhrov commented 4 weeks ago

wondering if it's the same as https://github.com/angular/angular/issues/37619