angular / angular

Deliver web apps with confidence πŸš€
https://angular.dev
MIT License
95.71k stars 25.26k forks source link

optional chaining in template uses 'null', but strictTemplates treats it as 'undefined' #37622

Open ajafff opened 4 years ago

ajafff commented 4 years ago

🐞 bug report

Affected Package

The issue is caused by package @angular/compiler or @angular/language-service? ### Is this a regression? no ### Description Using optional chaining (or safe navigation operator) in templates uses `null`, but the language-service and compiler (with strictTemplates) treat it as `undefined`. This is not a request to change `null` to `undefined` (that's proposed by #34385). Instead I want the static types to be correct so I'm not surprised by the different semantics of Angular's safe navigation operator compared to TypeScript/ECMAScript. ## πŸ”¬ Minimal Reproduction https://stackblitz.com/edit/angular-ivy-hh5mwz?file=src%2Fapp%2Fapp.component.ts

πŸ”₯ Exception or Error





🌍 Your Environment

Angular Version:


9.1.11


Anything else relevant?

thw0rted commented 4 years ago

This is pretty bad, it's not just that it's "using" null, it's creating one from nowhere. I usually think of optional-chaining as being shorthand for prop && prop.field (from your example StackBlitz). That would short-circuit the and-operator and return prop, which is undefined. Nothing was ever null so that's really unexpected (and IMHO bad!) behavior.

JoostK commented 4 years ago

Yep, definitely incorrect. The template checker should be using null given that is how it currently behaves at runtime.

@thw0rted Please capture your concern in #34385 as that is about the semantics, which this issue is not.

JoostK commented 4 years ago

We have decided not to land the fix, as it brings us further away from ES2020 optional chaining semantics, which would require the opposite breaking change in the future. No timeline is currently know for this effort.

Unfortunately this does mean that type checking is inaccurate until then :-(

danielkresta commented 2 years ago

Any updates on this?

I've discovered this when doing assignment to an input in strict mode:

<my-component
  [id]="obj?.id"
></my-component>

And in the component, the Input is defined as follows (and the strict template allows this as it expects T | undefined):

@Input() id: string | undefined

Yet the value becomes null because the compiled template looks like this:

('id',((ctx_r18.obj == null) ? null : ctx_r18._obj.id))

This does not look like a correct implementation of optional chaining.

The issue defeats the purpose of strict typing advocated by latest Angular versions, I'm surprised it exists for 2 years already.

JeanMeche commented 1 year ago

Quick standalone repro :

@Component({
  selector: 'my-app',
  standalone: true,
  imports:[JsonPipe],
  template: `
  undefined with optional chaining = {{test?.test|json}};`, // it's null 
})
export class App {
  test: undefined|{test: string} = undefined
}

bootstrapApplication(App);

This really isn't great since undefined is converted into null at runtime. This isnt' just a typing issue.

@danielkresta Your issue is actually discussed at #34385.

scibioulian commented 2 weeks ago

Any updates?