You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Jan 25, 2022. It is now read-only.
Copy file name to clipboardExpand all lines: README.md
+14-54Lines changed: 14 additions & 54 deletions
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,7 @@ Current Stage:
7
7
## Authors
8
8
* Claude Pache (@claudepache)
9
9
* Gabriel Isenberg (@the_gisenberg)
10
+
* Dustin Savery (@dustinsavery)
10
11
11
12
## Overview and motivation
12
13
When looking for a property value that's deep in a tree-like structure, one often has to check whether intermediate nodes exist:
@@ -29,19 +30,6 @@ var street = user.address?.street
29
30
var fooValue =myForm.querySelector('input[name=foo]')?.value
30
31
```
31
32
32
-
The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods:
33
-
34
-
```js
35
-
iterator.return?.() // manually close an iterator
36
-
```
37
-
or with methods not universally implemented:
38
-
```js
39
-
if (myForm.checkValidity?.() ===false) { // skip the test in older web browsers
40
-
// form validation fails
41
-
return;
42
-
}
43
-
```
44
-
45
33
## Prior Art
46
34
The following languages implement the operator with the same general semantics as this proposal (i.e., 1) guarding against a null base value, and 2) short-circuiting application to the whole chain):
47
35
* C#: [Null-conditional operator](https://msdn.microsoft.com/en-us/library/dn986595.aspx) — null-conditional member access or index, in read access.
@@ -58,7 +46,6 @@ The Optional Chaining operator is spelled `?.`. It may appear in three positions
58
46
```javascript
59
47
obj?.prop// optional static property access
60
48
obj?.[expr] // optional dynamic property access
61
-
func?.(...args) // optional function or method call
62
49
```
63
50
64
51
### Notes
@@ -71,27 +58,27 @@ If the operand at the left-hand side of the `?.` operator evaluates to undefined
71
58
72
59
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.)
73
60
```js
74
-
a?.b// undefined if `a` is null/undefined, `a.b` otherwise.
61
+
a?.b// undefined if `a` is null/undefined, `a.b` otherwise.
75
62
a ==null?undefined:a.b
76
63
77
-
a?.[x] // undefined if `a` is null/undefined, `a[x]` otherwise.
64
+
a?.[x] // undefined if `a` is null/undefined, `a[x]` otherwise.
78
65
a ==null?undefined: a[x]
79
66
80
-
a?.b() // undefined if `a` is null/undefined
67
+
a?.b() // undefined if `a` is null/undefined
81
68
a ==null?undefined:a.b() // throws a TypeError if `a.b` is not a function
82
-
// otherwise, evaluates to `a.b()`
69
+
// otherwise, evaluates to `a.b()`
83
70
84
-
a?.() // undefined if `a` is null/undefined
71
+
a?.() // undefined if `a` is null/undefined
85
72
a ==null?undefined:a() // throws a TypeError if `a` is neither null/undefined, nor a function
86
-
// invokes the function `a` otherwise
73
+
// invokes the function `a` otherwise
87
74
```
88
75
89
76
### Short-circuiting
90
77
91
78
If the expression on the LHS of `?.` evaluates to null/undefined, the RHS is not evaluated. This concept is called *short-circuiting*.
92
79
93
80
```js
94
-
a?.[++x] // `x` is incremented if and only if `a` is not null/undefined
81
+
a?.[++x] // `x` is incremented if and only if `a` is not null/undefined
95
82
a ==null?undefined: a[++x]
96
83
```
97
84
@@ -100,8 +87,8 @@ a == null ? undefined : a[++x]
100
87
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.
101
88
102
89
```js
103
-
a?.b.c(++x).d// if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
104
-
// otherwise, evaluates to `a.b.c(++x).d`.
90
+
a?.b.c(++x).d// if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
91
+
// otherwise, evaluates to `a.b.c(++x).d`.
105
92
a ==null?undefined:a.b.c(++x).d
106
93
```
107
94
@@ -134,37 +121,22 @@ That follows from the design choice of specifying the scope of short-circuiting
134
121
135
122
Note that, whatever the semantics are, there is no practical reason to use parentheses in that position anyway.
136
123
137
-
### Optional deletion
138
-
139
-
Because the `delete` operator is very liberal in what it accepts, we have that feature for free:
140
-
```js
141
-
delete a?.b
142
-
// delete (a == null ? undefined : a.b) // that *would* work if `? :` could return a Reference...
143
-
a ==null?undefined:deletea.b// this is what we get, really
144
-
```
145
-
146
124
## Not supported
147
125
148
-
Although they could be included for completeness, the following are not supported due to lack of real-world use cases or other compelling reasons; see [Issue # 22](https://github.com/tc39/proposal-optional-chaining/issues/22) and [Issue #54](https://github.com/tc39/proposal-optional-chaining/issues/54) for discussion:
126
+
Although they could be included for completeness, the following are not supported due to complexity, lack of real-world use cases, or other compelling reasons:
149
127
128
+
* optional function execution: `a?.()`
150
129
* optional construction: `newa?.()`
151
130
* optional template literal: ``a?.`{b}```
152
131
* constructor or template literals in/after an Optional Chain: `newa?.b()`, ``a?.b`{c}```
153
-
154
-
The following is not supported, although it has some use cases; see [Issue #18](//github.com/tc39/proposal-optional-chaining/issues/18) for discussion:
155
-
156
-
* optional property assignment: `a?.b= c`
132
+
* optional property assignment: `a?.b= x`
157
133
158
134
All the above cases will be forbidden by the grammar or by static semantics so that support might be added later.
159
135
160
136
## FAQ
161
137
162
-
[TODO: to be completed. In particular, discuss specific criticisms around long short-circuiting.]
163
-
164
-
165
138
<dl>
166
139
167
-
168
140
<dt>obj?.[expr] and func?.(arg) look ugly. Why not use obj?[expr] and func?(arg) as does <language X>?
169
141
170
142
<dd>
@@ -174,22 +146,10 @@ We don’t use the `obj?[expr]` and `func?(arg)` syntax, because of the difficul
174
146
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
147
176
148
* 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)`.
149
+
* extend the use of the recognisable `?.` sequence of characters to other cases: `obj?.[expr]`.
178
150
179
151
As for <language X>, it has different syntactical constraints than JavaScript because of <some construct not supported by X or working differently in X>.
180
152
181
-
182
-
183
-
<dt>Why does (null)?.b evaluate to undefined rather than null?
184
-
185
-
<dd>
186
-
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`.
188
-
189
-
In particular, the value `null` is considered to have no properties; therefore, `(null)?.b` is undefined.
0 commit comments