Optional Chaining for Javascript - ES proposal

30 November 2019 — Written by Amit Solanki
#javascript

We saw nullish coalescing yesterday, today let's see optional chaining another awesome ESnext feature. It is so awesome not to talk about.

Just like nullish coalescing, optional chaining helps us handle undefined and null cases. let's consider the following example

let company = {
  name: 'Github',
  revenue: 2000,
  users: [
    { name: 'John', handle: '@john' },
    { name: 'doe', handle: '@doe' },
  ],
  getUserNames() {
    return users.map(user => user.name)
  },
}

to access the values of this object, if they or the object can be undefined or null we would normally do the following.

const companyName =
  company !== undefined && company !== null ? company.name : undefined

In contrast with optional chaining we can do

const companyName = company?.name

The Syntax

obj?.prop // optional static property access
obj?.[expr] // optional dynamic property access
func?.(...args) // optional function or method call

Let's see how we can handle multiple ways of accessing properties of an object.

  1. Static
  2. Dynamic
  3. Function Call
  4. Dom access

Static Properties

let company = {
  name: 'Github',
  revenue: 2000,
  users: [
    { name: 'John', handle: '@john' },
    { name: 'doe', handle: '@doe' },
  ],
  getUserNames() {
    return users.map(user => user.name)
  },
}

// with optional Chaining
const companyName = company?.name

// without optional Chaining
const companyName =
  company !== undefined && company !== null ? company.name : undefined

Dynamic Properties

let company = {
  name: 'Github',
  revenue: 2000,
  users: [
    { name: 'John', handle: '@john' },
    { name: 'doe', handle: '@doe' },
  ],
  getUserNames() {
    return users.map(user => user.name)
  },
}

// with optional Chaining
const companyName = company?.['name']

// without optional Chaining
const companyName =
  company !== undefined && company !== null ? company['name'] : undefined

Function calls

let company = {
  name: 'Github',
  revenue: 2000,
  users: [
    { name: 'John', handle: '@john' },
    { name: 'doe', handle: '@doe' },
  ],
  getUserNames() {
    return users.map(user => user.name)
  },
}

// with optional Chaining
company.getUserNames?.()

// without optional Chaining
const companyName =
  company !== undefined &&
  company !== null &&
  Object.prototype.hasOwnProperty('getUserNames') &&
  typeof company.getUserNames === 'function'
    ? company['name']
    : undefined

Dom access

Optional Chaining supports DOM and its methods, so we can do stuff like this

const val = document.querySelector('input#name')?.value

Optional Chaining will always return undefined if the expression fails.

const value = company?.name

// 'value' will be undefined if company is undefined.

So we can pair optional chaining with nullish coalescing to handle those cases.

const value = company?.name ?? 'default name'

// 'value' will be 'default name' if either the company or name is undefined or null.

To wrap it up with a complete example

let company = {
  name: 'Github',
  revenue: 2000,
  users: [
    { name: 'John', handle: '@john' },
    { name: 'doe', handle: '@doe' },
  ],
  getUserNames() {
    return users.map(user => user.name)
  },
}

// Static access with default value
const value = company?.name ?? 'default name'

// Dynamic access with default value
const companyName = company?.['name'] ?? 'default value'

// Function call
company.getUserNames?.()

Optional chaining and nullish coalescing brings a lot of benefits, by reducing the complexity and making code readable. Give them a try and let me know your opinions in comments below.

You can use the following babel plugins to use them today.

Follow me on twitter

Amit Solanki