Optional chaining is coming to JavaScript. It’s going to change how we write code.

The Problem

We’ve all written code like this:

// Check if user has address
if (user && user.address && user.address.street) {
    console.log(user.address.street)
}

// Or with ternary
const street = user ? user.address ? user.address.street : undefined : undefined

// Or with try-catch
try {
    console.log(user.address.street)
} catch (e) {
    // Handle
}

Ugly and error-prone.

The Solution: Optional Chaining

// Just use ?.
const street = user?.address?.street

// If user or address is null/undefined, returns undefined
// No error!

How It Works

The ?. operator:

  • Returns undefined if left side is null/undefined
  • Otherwise, accesses the property
const user = {
    name: 'John',
    address: {
        street: 'Main St'
    }
}

user?.address?.street  // 'Main St'
user?.phone?.number    // undefined (no error!)

Optional Method Calls

// Call method if it exists
user.getName?.()

// Before
if (user.getName) {
    user.getName()
}

Optional Array Access

const users = [
    { name: 'John' },
    { name: 'Jane' }
]

users?.[0]?.name  // 'John'
users?.[5]?.name  // undefined

Real-World Examples

API Responses

// Before
const fetchUser = async (id) => {
    const response = await fetch(`/api/users/${id}`)
    const data = await response.json()
    
    const street = data && data.user && data.user.address && data.user.address.street
    return street
}

// After
const fetchUser = async (id) => {
    const response = await fetch(`/api/users/${id}`)
    const data = await response.json()
    
    return data?.user?.address?.street
}

Event Handlers

// Before
const handleClick = (event) => {
    if (event && event.target && event.target.dataset && event.target.dataset.id) {
        const id = event.target.dataset.id
        // Use id
    }
}

// After
const handleClick = (event) => {
    const id = event?.target?.dataset?.id
    if (id) {
        // Use id
    }
}

Configuration

// Before
const getConfig = () => {
    const theme = window.config && window.config.ui && window.config.ui.theme
    return theme || 'light'
}

// After
const getConfig = () => {
    return window.config?.ui?.theme ?? 'light'
}

Nullish Coalescing (??)

Bonus: ?? operator for default values.

// || has issues with falsy values
const count = 0
const value = count || 10  // 10 (wrong! 0 is valid)

// ?? only checks null/undefined
const value = count ?? 10  // 0 (correct!)

Difference from ||

const value1 = '' || 'default'   // 'default'
const value2 = '' ?? 'default'   // ''

const value3 = 0 || 10   // 10
const value4 = 0 ?? 10   // 0

const value5 = false || true   // true
const value6 = false ?? true   // false

Use ?? when 0, ”, or false are valid values.

Combining Both

const street = user?.address?.street ?? 'Unknown'

// If user or address is null/undefined, use 'Unknown'
// If street is null/undefined, use 'Unknown'
// If street is '', 0, or false, use that value

Browser Support

  • Chrome 80+
  • Firefox 74+
  • Safari 13.1+
  • Edge 80+

For older browsers, use Babel:

npm install --save-dev @babel/plugin-proposal-optional-chaining
npm install --save-dev @babel/plugin-proposal-nullish-coalescing-operator
// .babelrc
{
  "plugins": [
    "@babel/plugin-proposal-optional-chaining",
    "@babel/plugin-proposal-nullish-coalescing-operator"
  ]
}

TypeScript Support

TypeScript 3.7+ supports both:

interface User {
    name: string
    address?: {
        street?: string
    }
}

const user: User = { name: 'John' }

const street = user?.address?.street ?? 'Unknown'
// Type: string

Common Patterns

Nested Object Access

// Before
const city = data && data.user && data.user.address && data.user.address.city

// After
const city = data?.user?.address?.city

Function Calls

// Before
if (onSuccess && typeof onSuccess === 'function') {
    onSuccess(data)
}

// After
onSuccess?.(data)

Array Access

// Before
const firstUser = users && users.length > 0 && users[0]

// After
const firstUser = users?.[0]

Dynamic Property Access

const key = 'address'
const value = user?.[key]?.street

Should You Use It?

Yes, if:

  • Using Babel or TypeScript
  • Targeting modern browsers
  • Want cleaner code

Wait, if:

  • Need to support old browsers without transpilation
  • Team isn’t familiar with it yet

We’re using it in all new code. It’s a huge improvement.

The Verdict

Optional chaining and nullish coalescing are game-changers. They make JavaScript code cleaner and safer.

Start using them today (with Babel if needed).

Questions? Let me know!