Intermediate TypeScript, v2

Optional Chaining & Nullish Coalescing

Intermediate TypeScript, v2

Check out a free preview of the full Intermediate TypeScript, v2 course

The "Optional Chaining & Nullish Coalescing" Lesson is part of the full, Intermediate TypeScript, v2 course featured in this preview video. Here's what you'd learn in this lesson:

Mike explains the concept of optional chaining and demonstrates how to access nested properties using optional chaining. The concept of nullish coalescing and how it can be used to set default values for variables is also discussed in this segment.


Transcript from the "Optional Chaining & Nullish Coalescing" Lesson

>> Optional Chaining. So I'll set up an example, and we'll talk about why we might use optional chaining. Let me uncomment this real quick. So I have a little obligatory payment example, where we have a couple different objects here. And what I've done here, I have made an effort to create a highly nested object.

So, starting from the bottom, we have a response that could contain a single customer or an array of customers. And if we go one step up, we can see, all right, customers have a lastInvoice and an array of invoices. Presumably if they have no invoices, lastInvoice has no meaning, shouldn't be populated.

invoices have multiple payments, right? You can make a deposit, pay for half of the invoice, and you could pay the rest. And now same deal here, lastPayment, which like, if you haven't made any payments yet, clearly you don't have a last payment yet. And then a payment is sort of all the way at the bottom or all the way most nested into the JSON object.

It just has three properties on it. So, yeah, the important thing is just to know this contains this, contains this, contains this. So let's look at how we would write some code to deal with this. So this is one way of writing code. This is using just type guards, where we're using, at three steps along the way, destructured assignment to sort of pluck off a const variable that represents customer, lastInvoice, lastPayment, right?

And then once we check to make sure that customer is there, lastInvoice is there, lastPayment is there, then we can finally return the amount. So what we'll end up returning here is undefined, from the outer function we'll return undefined or a number. Let's see if that's true. There it is, number or undefined.

You could argue we could return null, but given what I just talked about, it would look the same, similar. So, but this is a lot of noise here, right? There's a lot of code. Here's how you could write it using optional chaining. It's similar to the non-null assertion operator, which was exclamation point dot, and this is question mark dot.

And look how we can just drill from data to customer to lastInvoice, lastPayment, amount. If at any of these steps something is found to be undefined or null, the entire expression will just evaluate to undefined. So this does not throw, this just evaluates to undefined if any of these things are undefined.

And so we can still return the same thing from our function, but look how much easier this code is to read. And we know what's gonna happen at each step of the way. This is much safer. This one I would use in production code that runs. It's different from the non-null assertion operator in that it's not gonna just randomly throw at a particular place, it's kinda like uncontrolled failure.

Here, we're evaluating out to exactly the same thing we'd evaluate out to here. That's part of why I didn't put null at each of these returns cuz I wanted it to match what you would get here. These two implementations are equivalent, but look how much easier it is to read.

So this is a great one to use. Also, I use this in tests, right? If you wanna drill all the way down to a particular property and it's not there, it's undefined. It's another way of sort of drilling into a nested object. Your choice depends on whether you wanna throw in the event of the failure or evaluate to undefined.

Last topic, Nullish Coalescing. So we have another example here. Let's say we have a video player that we're building. And we want to set the volume of a video player, and the volume could be 0, which represents mute. And then we have steps up by 25% all the way to 100.

And we have a function here that initializes the video player. And we'd say, if the type of the volume is undefined, then we set it to 50. Let's say that that's the default volume. Otherwise we return the config volume here. If we wanted to replace this, let's say that this ternary operator is getting a little unwieldy.

So this code is correct as it stands, but somebody comes along and they're not thinking about this carefully, and they're like, you know what I could do, is I've seen a pattern all over the place that looks like this. I'll just do that, right? This is a pattern I've seen all over the place.

It's good for setting defaults, who's seen something like this before? All right, we have a problem though. Look what happened to our allowed values for vol. 0 has disappeared. This kind of treatment for using a value if it exists and falling back to a default. You're doing a truthy-falsy check here.

And in this case, there is a value here that will fail the truthy-falsy check, but it's a valid thing, right? What this would leave you with is every time a user comes back, they have a preset volume, right? Every time if they muted it, they'd come back and they're back to 50.

So we don't want that, we can just change this to this, question marks, question mark, question mark. This is not a truthy-falsy check. This is a check for null or undefined. You look at vol, there's our 0 back where it should be. I would say, anytime you see this, you see pipe pipe, think.

Think carefully, but it's usually safe to switch it to question mark, question mark. Keep in mind, it's different behavior. And so you'll have to think carefully about what happened to the zeros, what happened to the empty strings, the nulls? But for new code that you're writing, this is a lot safer, right?

Because it's easy to think about truthy, falsy checks as sort of present or not present, but remember, Boolean false, 0, empty string. They all are falsy, and this handles falsy values in a way that's a little bit more appropriate. Yeah, I'm not gonna say just wholesale throughout your code, find double pipe and replace it, cuz somewhere someone might be depending on that behavior, the existing behavior.

But I tend to write all my code like this now, where you can really disambiguate between empty and present instead of truthy and falsy.
>> That is available just in null JavaScript as well, right?
>> It is. It is, right? It's a JavaScript feature. TypeScript handles the narrowing properly.

But it's still an important thing for working with null values, nullish values. That's a good question. In fact, optional chaining is also part of regular JavaScript. The non-null assertion operator, I think that's only TypeScript. I'd have to check, right, this bang.
>> I think so.
>> Did I lose?

I lost my other example, I've messed with this too much. But bang dot, that's only TypeScript. And this definite assignment operator is also only TypeScript.

Learn Straight from the Experts Who Shape the Modern Web

  • In-depth Courses
  • Industry Leading Experts
  • Learning Paths
  • Live Interactive Workshops
Get Unlimited Access Now