An excerpt from the repository, fetched Nov 13, 2026.
Read the project’s introduction
Optional Chaining for JavaScript
Status
ECMAScript proposal at stage 4 of the process.
Authors
Claude Pache (github)
Gabriel Isenberg (github, twitter)
Daniel Rosenwasser (github, twitter)
Dustin Savery (github, twitter)
Overview and motivation
When looking for a property value that's deep in a tree-like structure, one often has to check whether intermediate nodes exist:
var street = user.address && user.address.street;
Also, many API return either an object or null/undefined, and one may want to extract a property from the result only when it is not null:
var fooInput = myForm.querySelector('input[name=foo]')
var fooValue = fooInput ? fooInput.value : undefined
The Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves and/or assigning intermediate results in temporary variables:
var street = user.address?.street
var fooValue = myForm.querySelector('input[name=foo]')?.value
When some other value than undefined is desired for the missing case, this can usually be handled with the Nullish coalescing operator:
// falls back to a default value when response.settings is missing or nullish
// (response.settings == null) or when response.settings.animationDuration is missing
// or nullish (response.settings.animationDuration == null)
const animationDuration = response.settings?.animationDuration ?? 300;
The call variant of Optional Chaining is useful for dealing with interfaces that have optional methods:
iterator.return?.() // manually close an iterator
or with methods not universally implemented:
if (myForm.checkValidity?.() === false) { // skip the test in older web browsers
// form validation fails
return;
}
Prior Art
Unless otherwise noted, in the following languages, the syntax consists of a question mark prepending the operator, (a?.b, a?.b(), a?[b] or a?(b) when applicable).
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):
C#: Null-conditional operator — null-conditional member access or index, in read access.
Swift: Optional Chaining — optional property, method, or subscript call, in read and write access.
CoffeeScript: Existential operator — existential operator variant for property accessor, function call, object construction (new a?()). Also applies to assignment and deletion.
The following languages have a similar feature, but do not short-circuit the whole chain when it is longer than one element. This is justified by the fact that, in those languages, methods or properties might be legitimately used on null (e.g., null.toString() == "null" in Dart):
Kotlin: Safe calls — optional property access for read; optional property assignment for write.
Dart: Conditional member access — optional property access.
Ruby: Safe navigation operator — Spelled as: a&.b
The following languages have a similar feature. We haven’t checked whether they have significant differences in semantics with this proposal:
Groovy: Safe navigation operator
Angular: Safe navigation operator (link to archived documentation for Angular v10)
Syntax
The Optional Chaining operator is spelled ?.. It may appear in three positions:
obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call
Notes
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).
Semantics
Base case
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.
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 and that document.all should behave as an object.)
a?.b // undefined if `a` is null/undefined, `a.b` otherwise.
a == null ? undefined : a.b
a?.[x] // undefined if `a` is null/undefined, `a[x]` otherwise.
a == null ? undefined : a[x]
a?.b() // undefined if `a` is null/undefined
a == null ? undefined : a.b() // throws a TypeError if `a.b` is not a function
// otherwise, evaluates to `a.b()`
a?.() // undefined if `a` is null/undefined
a == null ? undefined : a() // throws a TypeError if `a` is neither null/undefined, nor a function
// invokes the function `a` otherwise
Short-circuiting
If the expression on the LHS of ?. evaluates to null/undefined, the RHS is not evaluated. This concept is called short-circuiting.
a?.[++x] // `x` is incremented if and only if `a` is not null/undefined
a == null ? undefined : a[++x]
Long short-circuiting
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.
a?.b.c(++x).d // if `a` is null/undefined, evaluates to undefined. Variable `x` is not incremented.
// otherwise, evaluates to `a.b.c(++x).d`.
a == null ? undefined : a.b.c(++x).d
Note that the check for nullity is made on a only. If, for example, a is not null, but a.b is null, a TypeError will be thrown when attempting to access the property "c" of a.b.
This feature is implemented by, e.g., C# and CoffeeScript; see Prior Art.
Stacking
Let’s call Optional Chain an Optional Chaining operator followed by a chain of property accesses, method or function calls.
An Optional Chain may be followed b
Read the full README on GitHub