Skip to Content
Course content

100: Optional Chaining and Nullish Coalescing

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I was digging through some API responses yesterday for a project, and I ran into a classic JavaScript headache. I had this user object coming back from a database, and I wanted to grab the user's Twitter handle. The structure looked something like this:

const user = {
  id: 42,
  profile: {
    socials: {
      twitter: '@dev_mindset'
    }
  }
};

The dreaded 'cannot read property of undefined'

Everything is great when the data is perfect. I can just call user.profile.socials.twitter and I get my string. But here's the thing: not every user fills out their profile. I tried running the same code against a user who hadn't set up their socials yet:

const user = { id: 43 }; 
console.log(user.profile.socials.twitter);

Boom. TypeError: Cannot read properties of undefined (reading 'socials'). The whole application crashes because I tried to look for socials inside of user.profile, but user.profile didn't even exist. It was undefined.

The clunky way to survive

Back in the day, we handled this with a long chain of && operators. It was our only defense. I'd write something like this:

const twitter = user && user.profile && user.profile.socials && user.profile.socials.twitter;

It works, sure. If any part of that chain is falsy, it stops and returns that falsy value instead of crashing. But honestly? It's a nightmare to read and write. If you have a deeply nested object, you end up repeating the same property names over and over. It's noisy.

A cleaner way to peek inside

This is where optional chaining (?.) comes in. It basically tells JavaScript: "Try to get this property, but if the thing before the dot is null or undefined, just stop and return undefined instead of throwing a fit."

Let's try that again with the broken user object:

const user = { id: 43 };
const twitter = user?.profile?.socials?.twitter;
console.log(twitter); // undefined

No crash. No long chain of &&. It's a massive quality-of-life improvement. I use this almost every time I touch a JSON response from an external API now.

The problem with the 'OR' default

Now, usually, I don't want undefined to show up in my UI. I want a fallback, like "No handle provided." My first instinct was to use the logical OR operator (||), which we've used for years to set defaults:

const handle = user?.profile?.socials?.twitter || 'No handle provided';

That works for undefined. But || has a quirk: it triggers for any falsy value. This includes 0, false, or an empty string "". Imagine if I were checking a user's followerCount. If a user actually has 0 followers, 0 || 'No data' would return 'No data'. That's a bug; 0 is a valid number, not "missing" data.

Handling only the 'missing' stuff

To fix this, we use the Nullish Coalescing operator (??). Unlike ||, the ?? operator only kicks in if the value is null or undefined. It ignores other falsy values like 0 or "".

Let's see the difference in action:

const settings = {
  volume: 0,
  theme: ""
};

const vol = settings.volume || 50; // Result: 50 (Wrong! User wanted 0)
const volFixed = settings.volume ?? 50; // Result: 0 (Correct!)

const theme = settings.theme || 'dark'; // Result: 'dark' (Wrong! User wanted empty)
const themeFixed = settings.theme ?? 'dark'; // Result: "" (Correct!)

When you combine these two—optional chaining to safely navigate and nullish coalescing to set a sensible default—you get code that is both resilient and concise.




📋 Practical Task

Building a Game Character Stat Fallback System

You are building a character sheet for an RPG. Some characters have specialized "Buffs" (extra stats), while others don't. If a stat is missing, it should default to 0. However, if a stat is explicitly set to 0, it should stay 0.

Complete the getStat function. It should safely attempt to access character.buffs.strength. If any part of that path is missing, or if the final value is null/undefined, return 0.

const characterA = {
  name: "Warrior",
  buffs: {
    strength: 10,
    agility: 2
  }
};

const characterB = {
  name: "Mage",
  buffs: {
    strength: 0, // This is a valid stat!
    intelligence: 20
  }
};

const characterC = {
  name: "Villager"
  // No buffs object at all
};

function getStat(character) {
  // YOUR CODE HERE: Use optional chaining and nullish coalescing
}

console.log(getStat(characterA)); // Expected: 10
console.log(getStat(characterB)); // Expected: 0
console.log(getStat(characterC)); // Expected: 0
Rating
0 0

There are no comments for now.

to be the first to leave a comment.