JavaScript Optional Chaining: Finally, No More 'Cannot Read Property of Undefined'
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!