Skip to content
This repository was archived by the owner on Jan 25, 2022. It is now read-only.

Commit 31d3e1f

Browse files
committed
Normative: Switch syntax to ??.
Follows the conclusion of the discussion at #34
1 parent 176aa09 commit 31d3e1f

File tree

2 files changed

+52
-76
lines changed

2 files changed

+52
-76
lines changed

README.md

Lines changed: 32 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,18 @@ var fooValue = fooInput ? fooInput.value : undefined
2525
The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:
2626

2727
```javascript
28-
var street = user.address?.street
29-
var fooValue = myForm.querySelector('input[name=foo]')?.value
28+
var street = user.address??.street
29+
var fooValue = myForm.querySelector('input[name=foo]')??.value
3030
```
3131
3232
The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods:
3333
3434
```js
35-
iterator.return?.() // manually close an iterator
35+
iterator.return??() // manually close an iterator
3636
```
3737
or with methods not universally implemented:
3838
```js
39-
if (myForm.checkValidity?.() === false) { // skip the test in older web browsers
39+
if (myForm.checkValidity??() === false) { // skip the test in older web browsers
4040
// form validation fails
4141
return;
4242
}
@@ -54,44 +54,45 @@ The following languages have a similar feature. We haven’t checked whether the
5454
5555
## Syntax
5656
57-
The Optional Chaining operator is spelled `?.`. It may appear in three positions:
57+
The Optional Chaining operator is spelled `??`. It may appear in three positions:
5858
```javascript
59-
obj?.prop // optional static property access
60-
obj?.[expr] // optional dynamic property access
61-
func?.(...args) // optional function or method call
59+
obj??.prop // optional static property access
60+
obj??[expr] // optional dynamic property access
61+
func??(...args) // optional function or method call
6262
```
6363
64-
### Notes
65-
* In order to allow `foo?.3:0` to be parsed as `foo ? .3 : 0` (as required for backward compatibility), a simple lookahead is added at the level of the lexical grammar, so that the sequence of characters `?.` is not interpreted as a single token in that situation (the `?.` token must not be immediately followed by a decimal digit).
64+
### Why two question marks instead of one?
65+
66+
An earlier version of this proposal used `??.` for the optional chaining operator, with `??[` for dynamic property access and `??(` for optional function/method calls. The switch to two question marks allows for more consistency and the ability to omit the surprising `.` in the latter two cases. See [the past discussion](https://github.com/tc39/proposal-optional-chaining/issues/34) on this topic for more details.
6667
6768
## Semantics
6869
6970
### Base case
70-
If the operand at the left-hand side of the `?.` operator evaluates to undefined or null, the expression evaluates to undefined. Otherwise the targeted property access, method or function call is triggered normally.
71+
If the operand at the left-hand side of the `??` operator evaluates to undefined or null, the expression evaluates to undefined. Otherwise the targeted property access, method or function call is triggered normally.
7172
7273
Here are basic examples, each one followed by its desugaring. (The desugaring is not exact in the sense that the LHS should be evaluated only once.)
7374
```js
74-
a?.b         // undefined if `a` is null/undefined, `a.b` otherwise.
75+
a??.b         // undefined if `a` is null/undefined, `a.b` otherwise.
7576
a == null ? undefined : a.b
7677

77-
a?.[x]       // undefined if `a` is null/undefined, `a[x]` otherwise.
78+
a??[x]       // undefined if `a` is null/undefined, `a[x]` otherwise.
7879
a == null ? undefined : a[x]
7980

80-
a?.b()       // undefined if `a` is null/undefined
81+
a??.b()       // undefined if `a` is null/undefined
8182
a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function
8283
                            // otherwise, evaluates to `a.b()`
8384

84-
a?.()       // undefined if `a` is null/undefined
85+
a??()       // undefined if `a` is null/undefined
8586
a == null ? undefined : a() // throws a TypeError if `a` is neither null/undefined, nor a function
8687
            // invokes the function `a` otherwise
8788
```
8889
8990
### Short-circuiting
9091
91-
If the expression on the LHS of `?.` evaluates to null/undefined, the RHS is not evaluated. This concept is called *short-circuiting*.
92+
If the expression on the LHS of `??.` evaluates to null/undefined, the RHS is not evaluated. This concept is called *short-circuiting*.
9293
9394
```js
94-
a?.[++x]         // `x` is incremented if and only if `a` is not null/undefined
95+
a??[++x]         // `x` is incremented if and only if `a` is not null/undefined
9596
a == null ? undefined : a[++x]
9697
```
9798
@@ -100,7 +101,7 @@ a == null ? undefined : a[++x]
100101
In fact, short-circuiting, when triggered, skips not only the current property access, method or function call, but also the whole chain of property accesses, method or function calls directly following the Optional Chaining operator.
101102
102103
```js
103-
a?.b.c(++x).d  // if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
104+
a??.b.c(++x).d  // if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
104105
              // otherwise, evaluates to `a.b.c(++x).d`.
105106
a == null ? undefined : a.b.c(++x).d
106107
```
@@ -116,7 +117,7 @@ Let’s call *Optional Chain* an Optional Chaining operator followed by a chain
116117
An Optional Chain may be followed by another Optional Chain.
117118
118119
```js
119-
a?.b[3].c?.(x).d
120+
a??.b[3].c??(x).d
120121
a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
121122
// (as always, except that `a` and `a.b[3].c` are evaluated only once)
122123
```
@@ -126,7 +127,7 @@ a == null ? undefined : a.b[3].c == null ? undefined : a.b[3].c(x).d
126127
Parentheses limit the scope of short-circuting:
127128
128129
```js
129-
(a?.b).c
130+
(a??.b).c
130131
(a == null ? undefined : a.b).c
131132
```
132133
@@ -138,7 +139,7 @@ Note that, whatever the semantics are, there is no practical reason to use paren
138139
139140
Because the `delete` operator is very liberal in what it accepts, we have that feature for free:
140141
```js
141-
delete a?.b
142+
delete a??.b
142143
// delete (a == null ? undefined : a.b) // that *would* work if `? :` could return a Reference...
143144
a == null ? undefined : delete a.b // this is what we get, really
144145
```
@@ -147,13 +148,13 @@ a == null ? undefined : delete a.b // this is what we get, really
147148
148149
The following are not supported due to a lack of real-world use cases:
149150
150-
* optional construction: `new a?.()`
151-
* optional template literal: ``a?.`{b}` ``
152-
* constructor or template literals in/after an Optional Chain: `new a?.b()`, ``a?.b`{c}` ``
151+
* optional construction: `new a??()`
152+
* optional template literal: ``a??`{b}` ``
153+
* constructor or template literals in/after an Optional Chain: `new a??b()`, ``a??.b`{c}` ``
153154
154155
The following is not supported, although it has some use cases; see [Issue #18](//github.com/tc39/proposal-optional-chaining/issues/18) for discussion:
155156
156-
* optional property assignment: `a?.b = c`
157+
* optional property assignment: `a??.b = c`
157158
158159
All the above cases will be forbidden by the grammar or by static semantics so that support might be added later.
159160
@@ -165,28 +166,13 @@ All the above cases will be forbidden by the grammar or by static semantics so t
165166
<dl>
166167
167168
168-
<dt>obj?.[expr] and func?.(arg) look ugly. Why not use obj?[expr] and func?(arg) as does &lt;language X>?
169-
170-
<dd>
171-
172-
We don’t use the `obj?[expr]` and `func?(arg)` syntax, because of the difficulty for the parser to efficiently distinguish those forms from the conditional operator, e.g., `obj?[expr].filter(fun):0` and `func?(x - 2) + 3 :1`.
173-
174-
Alternative syntaxes for those two cases each have their own flaws; and deciding which one looks the least bad is mostly a question of personal taste. Here is how we made our choice:
175-
176-
* pick the best syntax for the `obj?.prop` case, which is expected to occur most often;
177-
* extend the use of the recognisable `?.` sequence of characters to other cases: `obj?.[expr]`, `func?.(arg)`.
178-
179-
As for &lt;language X>, it has different syntactical constraints than JavaScript because of &lt;some construct not supported by X or working differently in X>.
180-
181-
182-
183-
<dt>Why does (null)?.b evaluate to undefined rather than null?
169+
<dt>Why does (null)??.b evaluate to undefined rather than null?
184170
185171
<dd>
186172
187-
Neither `a.b` nor `a?.b` is intended to preserve arbitrary information on the base object `a`, but only to give information about the property `"b"` of that object. If a property `"b"` is absent from `a`, this is reflected by `a.b === undefined` and `a?.b === undefined`.
173+
Neither `a.b` nor `a??.b` is intended to preserve arbitrary information on the base object `a`, but only to give information about the property `"b"` of that object. If a property `"b"` is absent from `a`, this is reflected by `a.b === undefined` and `a??.b === undefined`.
188174
189-
In particular, the value `null` is considered to have no properties; therefore, `(null)?.b` is undefined.
175+
In particular, the value `null` is considered to have no properties; therefore, `(null)??.b` is undefined.
190176
191177
192178
@@ -198,19 +184,19 @@ See [Issue #3 (comment)](https://github.com/tc39/proposal-optional-chaining/issu
198184
199185
200186
201-
<dt>In a?.b.c, if a.b is null, then a.b.c will evaluate to undefined, right?
187+
<dt>In a??.b.c, if a.b is null, then a.b.c will evaluate to undefined, right?
202188
203189
<dd>
204190
205191
No. It will throw a TypeError when attempting to fetch the property `"c"` of `a.b`.
206192
207193
The opportunity of short-circuiting happens only at one time, just after having evaluated the LHS of the Optional Chaining operator. If the result of that check is negative, evaluation proceeds normally.
208194
209-
In other words, the `?.` operator has an effect only at the very moment it is evaluated. It does not change the semantics of subsequent property accesses, method or function calls.
195+
In other words, the `??.` operator has an effect only at the very moment it is evaluated. It does not change the semantics of subsequent property accesses, method or function calls.
210196
211197
212198
213-
<dt>In a deeply nested chain like `a?.b?.c`, why should I write `?.` at each level? Should I not be able to write the operator only once for the whole chain?</dt>
199+
<dt>In a deeply nested chain like `a??.b??.c`, why should I write `??.` at each level? Should I not be able to write the operator only once for the whole chain?</dt>
214200
215201
<dd>
216202

spec.html

Lines changed: 20 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<h1>Scope</h1>
2323
<p>This is the spec text of the <a href="https://github.com/tc39/proposal-optional-chaining/">Optional Chaining proposal</a> in ECMAScript. </p>
2424

25-
<p>For the syntax, we use the `?.` token, with a lookahead at the level of the lexical grammar that allows to discriminate between `a?.b` (optional chaining) and `a?.3:0` (conditional operator, whose meaning cannot be changed due to backward compatibility constraints).</p>
25+
<p>For the syntax, we use the `??.` token. An earlier version of this proposal used `?.`</p>
2626

2727
<p>An <a href="https://claudepache.github.io/es-optional-chaining/">early version of this proposal</a> used a Nil reference to express short-circuiting. This one is based on syntax only.</p>
2828

@@ -50,11 +50,7 @@ <h1>Punctuators (<a href="https://tc39.github.io/ecma262/#sec-punctuators">11.7<
5050
<h2>Syntax</h2>
5151
<emu-grammar>
5252
<ins class="block">
53-
OptionalChainingPunctuator ::
54-
`?.` [lookahead &lt;! DecimalDigit
55-
</ins>
56-
57-
OtherPunctuator :: one of
53+
Punctuator ::
5854
`{` `(` `)` `[` `]`
5955
`.` `...` `;` `,`
6056
`&lt;` `&gt;` `&lt;=` `&gt;=`
@@ -68,17 +64,7 @@ <h2>Syntax</h2>
6864
`?` `:`
6965
`=` `+=` `-=` `*=` `%=` `**=` `&lt;&lt;=` `&gt;&gt;=` `&gt;&gt;&gt;=` `&amp;=` `|=` `^=`
7066
`=&gt;`
71-
72-
Punctuator ::
73-
<ins>OptionalChainingPunctuator</ins>
74-
OtherPunctuator
75-
76-
DivPunctuator ::
77-
`/`
78-
`/=`
79-
80-
RightBracePunctuator ::
81-
`}`
67+
`??.` `??(` `??[`
8268
</emu-grammar>
8369
</emu-clause>
8470
<!--
@@ -148,9 +134,9 @@ <h2>Syntax</h2>
148134
OptionalExpression[?Yield, ?Await] OptionalChain[?Yield, ?Await]
149135

150136
OptionalChain[Yield, Await] :
151-
OptionalChainingPunctuator `[` Expression[+In, ?Yield, ?Await] `]`
152-
OptionalChainingPunctuator IdentifierName
153-
OptionalChainingPunctuator Arguments[?Yield, ?Await]
137+
`??[` Expression[+In, ?Yield, ?Await] `]`
138+
`??.` IdentifierName
139+
`??(` ArgumentsList[?Yield, ?Await] `,`? `)
154140
OptionalChain[?Yield, ?Await] `[` Expression[+In, ?Yield, ?Await] `]`
155141
OptionalChain[?Yield, ?Await] `.` IdentifierName
156142
OptionalChain[?Yield, ?Await] Arguments[?Yield, ?Await]
@@ -209,7 +195,7 @@ <h1>Static Semantics: Contains</h1>
209195
1. Return *false*.
210196
</emu-alg>
211197
<ins class="block">
212-
<emu-grammar>OptionalChain : OptionalChainingPunctuator IdentifierName</emu-grammar>
198+
<emu-grammar>OptionalChain : `??.` IdentifierName</emu-grammar>
213199
<emu-alg>
214200
1. If _symbol_ is a |ReservedWord|, return *false*.
215201
1. If _symbol_ is an |Identifier| and StringValue of _symbol_ is the same value as the StringValue of |IdentifierName|, return *true*.
@@ -535,7 +521,7 @@ <h1>Tagged Templates (<a href="https://tc39.github.io/ecma262/#sec-tagged-templa
535521
<ins class="block">
536522
<emu-clause id="sec-optional-chains">
537523
<h1>Optional Chains</h1>
538-
<emu-note>An optional chain is a chain of property accesses and function calls introduced by an |OptionalChainingPunctuator|.</emu-note>
524+
<emu-note>An optional chain is a chain of property accesses and function calls introduced by `??.`, `??(` or `??[`.</emu-note>
539525
</emu-note>
540526
<emu-clause id="sec-optional-chaining-evaluation">
541527
<h1>Runtime Semantics: Evaluation</h1>
@@ -558,23 +544,27 @@ <h1>Runtime Semantics: Evaluation</h1>
558544
<emu-clause id="sec-optional-chaining-chain-evaluation">
559545
<h1>Runtime Semantics: ChainEvaluation</h1>
560546
<p>With parameters _baseValue_ and _baseReference_.</p>
561-
<emu-grammar>OptionalChain : OptionalChainingPunctuator `[` Expression `]`</emu-grammar>
547+
<emu-grammar>OptionalChain : `??[` Expression `]`</emu-grammar>
562548
<emu-alg>
563549
1. If the code matched by this production is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
564550
1. Return ? EvaluateDynamicPropertyAccess(_baseValue_, |Expression|, _strict_).
565551
</emu-alg>
566-
<emu-grammar>OptionalChain : OptionalChainingPunctuator IdentifierName</emu-grammar>
552+
<emu-grammar>OptionalChain : `??.` IdentifierName</emu-grammar>
567553
<emu-alg>
568554
1. If the code matched by this production is strict mode code, let _strict_ be *true*, else let _strict_ be *false*.
569555
1. Return ? EvaluateStaticPropertyAccess(_baseValue_, |IdentifierName|, _strict_).
570556
</emu-alg>
571-
<emu-grammar>OptionalChain : OptionalChainingPunctuator Arguments</emu-grammar>
557+
<emu-grammar>
558+
OptionalChain :
559+
`??(` ArgumentsList? `)`
560+
</emu-grammar>
561+
<emu-grammar>OptionalChain : `??(` ArgumentsList `,` `)` </emu-grammar>
572562
<emu-alg>
573563
1. Let _thisChain_ be this production.
574564
1. Let _tailCall_ be IsInTailPosition(_thisChain_).
575-
1. Return ? EvaluateCall(_baseValue_, _baseReference_, |Arguments|, _tailCall_).
565+
1. Return ? EvaluateCall(_baseValue_, _baseReference_, |ArgumentsList|, _tailCall_).
576566
</emu-alg>
577-
<emu-grammar>OptionalChain : OptionalChain `[` Expression `]`</emu-grammar>
567+
<emu-grammar>OptionalChain : `??[` Expression `]`</emu-grammar>
578568
<emu-alg>
579569
1. Let _optionalChain_ be this |OptionalChain|.
580570
1. Let _newReference_ be ? _optionalChain_.ChainEvaluation(_baseValue_, _baseReference_).
@@ -642,8 +632,8 @@ <h1>Expression Rules</h1>
642632
</emu-alg>
643633
<emu-grammar>
644634
OptionalChain :
645-
OptionalChainingPunctuator `[` Expression `]`
646-
OptionalChainingPunctuator IdentifierName
635+
`??[` Expression `]`
636+
`??.` IdentifierName
647637
OptionalChain `[` Expression `]`
648638
OptionalChain `.` IdentifierName
649639
</emu-grammar>
@@ -652,7 +642,7 @@ <h1>Expression Rules</h1>
652642
</emu-alg>
653643
<emu-grammar>
654644
OptionalChain :
655-
OptionalChainingPunctuator Arguments
645+
`??(` ArgumentsList `,`? `)`
656646
OptionalChain Arguments
657647
</emu-grammar>
658648
<emu-alg>

0 commit comments

Comments
 (0)