-
Notifications
You must be signed in to change notification settings - Fork 25.6k
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
Align with the optional chaining spec #34385
Comments
Just to check, does this cover the case of array-index optional chaining ( |
It would be very interesting to use optional chaining in arrays, following the TS spec: In Angular Templates, arr?.[0] causes a: zone-evergreen.js:659 Unhandled Promise rejection: Template parse errors: |
TL;DR: Angular Templates resolves to Consider the following: Component Typescript public preferences: {email: string};
public getPreferencesEmail() {
return this.preferences?.email;
}
public ngOnInit() {
this.preferencesService.subscribe(preferences =>
(this.preferences = preferences));
} Example A HTML <div *ngIf="preferences?.email === undefined; else emailInput">Loading...</div>
<ng-template #emailInput>
<input id="email" [value]="preferences.email">
</ng-template> Example B HTML <div *ngIf="getPreferencesEmail() === undefined; else emailInput">Loading...</div>
<ng-template #emailInput>
<input id="email" [value]="getPreferencesEmail()">
</ng-template> In Example A, the loading div does not show because Angular Templates evaluates the safe-navigation to null. In Example B, the loading div does show because TypeScript evaluates the optional chaining to undefined. If undefined means "Loading", null means "Not set by user" and anything else is "a user-set value"; then angular's safe navigation introduces a bug in our code. This can be even more confusing given that |
The problem with angular's syntax is that there's no way to check that the array isn't undefined or null. Yes, what you show will check to see if the array has that indexed item, but if I want to know that the property is in fact initialized, I have to do
It would be much better to use optional chaining and just use
|
Another thing to note: since TypeScript 3.9 non-null-assertions don't end the optional chain foo?.bar!.baz;
// ts@<3.9
(foo?.bar).baz; // asserts that `foo?.bar` is non-null - which obviously doesn't make sense
// ts@>=3.9
(foo?.bar.baz); // asserts that if `foo` is non-null, its `bar` property will also be non-null |
Hi, For me it shows this:
|
In my case
Display error: Template parse errors: |
Both the language service and the compiler errors on the above.
This makes the "safe navigated in templates" irrelevant as the compiler/type checker doesn't allow it. The proper ES syntax ( |
This leads to very inconsistent behavior. I used the exact same code both in *ngIf and if in code: foo?.bar <= 0 |
@petebacondarwin since you're eyes-on here, and it's been quite some time since this issue was really active, do you think you could get a status for this from the team? Maybe assign a priority? |
@thw0rted - we definitely want to move to ECMAScript compliant behaviour, but since this would be a breaking change, we would also need to consider how to migrate users and it would have to happen in a major release. This is unlikely to happen for v13 now, but I will raise it in our next framework sync up meeting in two weeks. |
since there are finite rules around how angular does optional chaining, it would be a relatively easy regex replace rule for the most part, right? i know, it's always easy to say something's easy :) |
I think it is definitely possible to write a migration for these cases, but I don't think it is trivial. |
This is being tracked in the aggregate issue of #43485 for which we need to create a project proposal. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
The really annoying thing about this is that it breaks the type system, because compile-time type checks think For example, if you have a component with an Input of a type that allows What's worse, if you change the input type to allow null and disallow undefined, then the compiler fails as it thinks the optional chaining syntax may return undefined, even though it actually returns null:
|
Another workaround is to use the "slice" or the "at" method:
|
we just run into this issue and would love to see a fix for it. ❤️ |
Sadly the new control flow syntax did not change the behavior :( |
🚀 feature request
Relevant Package
@angular/compiler
Description
Optional chaining[1] reached stage 4. We've been supporting similar syntax in templates for a while now, calling it the "safe navigation operator"[2]. For simplicity and smaller payload, we can consider aligning with the spec in future versions of the framework.
There are a couple of semantical and syntactical differences between optional chaining and safe navigation.
Syntax
Optional chaining has the following syntax:
Safe navigation supports only direct property access. Optional chaining supports this, as well as, method calls and function calls. Function calls are particularly useful in iterators:
Semantics
With optional chaining, the expression
a?.b
will be translated toa == null ? undefined : a.b
. In Angular, the semantics of the same expression would benull == a ? null : a.b
.If
a
isnull
orundefined
, the expressiontypeof a?.b
would evaluate to"object"
with optional chaining and"undefined"
in Angular's safe navigation operator.Except the mentioned difference above, method calls are compiled similarly:
In both, optional chaining and safe navigation in templates, stacking the operators is translated the same way:
(a?.b).c?.d
becomesnull == a ? null : null == a.b.c ? null : a.b.c.d
.Another difference seems to be the way parentheses are handled. The optional chaining spec defines that
null==e.foo?null:e.foo.b.c
should be translated to(a == null ? undefined : a.b).c
. In Angular the same expression translates tonull == a ? null : a.b.c
.PS: looks like the last issue is fixed by #34221.
[1] Optional chaining spec https://github.com/tc39/proposal-optional-chaining
[2] Safe navigation https://angular.io/guide/template-syntax#safe-navigation-operator
The text was updated successfully, but these errors were encountered: