angular: strictTemplates and strictNullChecks doesn't work well with optional chaining in templates

🐞 bug report

Affected Package

The issue is caused by package @angular/compiler?

Is this a regression?

?

Description

  • enable Ivy
  • enable strictTemplates
  • enable strictNullChecks
  • use an expression like foo?.bar.baz in a template
    • foo is nullable, bar is not
    • template compiler shows error on baz: Object is possibly 'undefined'.
  • note that the whole chain ends if foo is nullish (long short circuiting), therefore accessing baz is always safe
  • disabling strictNullChecks makes the compile error go away and everything functions as expected without runtime error (i.e. Angular’s optional chaining also does long short circuiting)

🔬 Minimal Reproduction

https://stackblitz.com/edit/angular-ivy-pu8dk5

🔥 Exception or Error


`Object is possibly 'undefined'.`

🌍 Your Environment

Angular Version:


@angular/animations 9.1.11
@angular/common 9.1.11
@angular/compiler 9.1.11
@angular/core 9.1.11
@angular/forms 9.1.11
@angular/platform-browser 9.1.11
@angular/platform-browser-dynamic 9.1.11
@angular/router 9.1.11

Anything else relevant?

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 16
  • Comments: 21 (8 by maintainers)

Most upvoted comments

Reproduction in angular 12.1 https://github.com/sod/ng-strict-issue1 / https://github.com/sod/ng-strict-issue1/blob/master/src/app/app.component.ts

Right now as soon as you use the operator in the template, angular forces you to use it everywhere in that statement, even though, nested structures may be guaranteed to not be undefined by the types.

@JoostK any updates on this?

I think I have similar issue

stackblitz: link

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 tempalte 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>

Perhaps this tree reshaping logic from the compiler should be duplicated in the typechecker. Or maybe perform it once - before even passing the AST to compiler and typechecker.

Indeed, the AST restructuring logic happens in the compiler’s output emitter, i.e. the transform from “expression AST” to “output AST”. The typechecker generates TypeScript AST nodes directly from “expression AST” nodes, so it hasn’t gone through that transformation.

FYI I did attempt expanding the translation in the type-checker at one point but never got to finish it; I should have a look at that again.

still an issue on angular 11, I’m bypassing it without using optional chaining, like *ngIf="foo && foo.bar.baz"