Optional chaining (?.) Optional Chaining for JavaScript ( https://github.com/tc39/proposal-optional-chaining )

Authors

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

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

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.

Optional chaining ( https://www.martinmck.com/posts/es2020-everything-you-need-to-know/ )

Optional chaining is probably one of the most highly anticipated features to come to JavaScript in quite some time.

In terms of impact on cleaner JavaScript code, this one scores very highly .

When checking for a property deep inside a nested object, you often have to check for the existence of intermediate objects.

Let’s work through an example.

const test = {
  name: "foo",
  age: 25,
  address: {
    number: 44,
    street: "Sesame Street",
    city: {
      name: "Fake City",
      lat: 40,
      lon: 74
    }
  }
}

// when we want to check for the name of the city
if (test.address.city.name) {
  console.log("City name exists!");
}

// City Name exists!

This works fine! But in software, we can’t always rely on the happy path.

Sometimes intermediate values will not exist. Let’s look at the same example, but with no city value defined.

const test = {
  name: "foo",
  age: 25,
  address: {
    number: 44,
    street: "Sesame Street"
  }
}

if (test.address.city.name) {
  console.log("City name exists!");
}

// TypeError: Cannot read property 'name' of undefined

Our code is broken. This happens because we are trying to access name on test.address.city which is undefined. When you attempt to read a property on undefined, the above TypeError will be thrown.

How do we fix this? In a lot of JavaScript code, you will see the following solution.

const test = {
  name: "foo",
  age: 25,
  address: {
    number: 44,
    street: "Sesame Street"
  },

}

if (test.address && test.address.city && test.address.city.name) {
  console.log("City name exists!");
}

// no TypeError thrown!

Our code now runs, but we’ve had to write quite a bit of code there to solve the problem.

We can do better .

The Optional Chaining operator of ES2020 allows you to check if a value exists deep inside an object using the new ? syntax .

Here is the above example rewritten using the optional chaining operator.

const test = {
  name: "foo",
  age: 25,
  address: {
    number: 44,
    street: "Sesame Street"
  },

}

// much cleaner.
if (test?.address?.city?.name) {
  console.log("City name exists!");
}

// no TypeError thrown!

Looking good. We have condensed the long && chains into our much more succinct and readable optional chaining operator. If any of the values along the chain are null or undefined, the expression simply returns undefined.

The optional chaining operator is very powerful.

Have a look at the following examples to see other ways it can be used.

const nestedProp = obj?.['prop' + 'Name']; // computed properties

const result = obj.customMethod?.(); // functions

const arrayItem = arr?.[42]; // arrays

Articles

https://javascript.info

Slides

https://www.freecodecamp.org

Optional chaining syntax allows you to access deeply nested object properties without worrying if the property exists or not.

If it exists, great! If not, undefined will be returned.

This not only works on object properties, but also on function calls and arrays.

https://www.jesuisundev.com

L’optional chaining fait aussi partie de l’ES2020. Et ça donne du plaisir !

Quand tu dois accéder aux propriétés imbriquées dans un objet, t’es obligé d’être super prudent.

Si tu commences à accéder à une propriété sur un objet qui n’existe pas, ça te pète à la gueule immédiatement

const superToto = {
    hero : true,
    location: {
        city: {
          name: "Lyon"
        }
    },
    power: {
        psychic: ['telekinesis']
    }
}

if(superToto.power && superToto.power.psychic) {
    console.log(superToto.power.psychic) //['telekinesis']
}

console.log(superToto.location.country.name) // throw error

L’optional chaining règle ce problème en faisait la vérification à ta place et en renvoyant undefined si la propriété n’existe pas. Il suffit de suivre la convention.

const superToto = {
    hero : true,
    location: {
        city: {
          name: "Lyon"
        }
    },
    power: {
        psychic: ['telekinesis']
    }
}

if(superToto?.power?.psychic) {
    console.log(superToto.power.psychic) //['telekinesis']
}

console.log(superToto?.location?.country?.name) // undefined

Fini les prises de têtes à vérifier chaque propriété avant de l’utiliser de peur de se prendre une grosse erreur dans la face.

L’optional chaining nous permet de faire ça safe, de façon super pratique et super simple à lire. J’étais fou quand j’ai vu ça !

https://2ality.com