My team just upgraded one of our big apps to Angular 9, and along with that came an Upgrade to the latest version of TypeScript. It includes something called optional chaining. This is something very similar to the Safe Navigation operator I wrote about last week. The Safe Navigation Operator is part of the Angular framework and allows you to parse nested object properties in an HTML view without errors, even if one of the objects is undefined or null. It is a great way to avoid initialization headaches.

The same approach can be used in TypeScript.

The Setup

Although this is a TypeScript specific issue, i'm going to use the same sample I used in my previous article. I created a class for an InnerData and one for an OuterData to represent nested data:

view plain print about
1class InnerData {
2 public data = 'default';
3}
4class OuterData {
5 public inner: InnerData;
6}

Then I created a bunch of variables of type OuterData as part of my component class.

view plain print about
1public populatedInstance: OuterData;
2 public nullInstance: OuterData;
3 public nullInner: OuterData;
4 public nullInnerData: OuterData;

Then, in the constructor I initialized the variables based on their value.

The nullInstance value will not be defaulted to anything, so we do nothing with that.

The populatedInstance will include the InnerData property and the InnerData.data property:

view plain print about
1this.populatedInstance = new OuterData();
2 this.populatedInstance.inner = new InnerData();
3 this.populatedInstance.inner.data = 'name';

The nullInner variable will not create the Inner value:

view plain print about
1this.nullInner = new OuterData();

Now, let's go ahead and create the null inner data:

view plain print about
1this.nullInnerData = new OuterData();
2 this.nullInnerData.inner = new InnerData();
3 delete this.nullInnerData.inner.data;

The class defaulted the data value on the InnerData object, so this purposely removes that object so it is non existent.

That is the setup, let's see how to use it:

Using Optional Chaining

In our code we can easily access the populated instance:

view plain print about
1console.log('populatedInstance');
2 console.log(this.populatedInstance.inner.data);

That works without issues.

But, when we try to access the same nested path using the nullInner object:

view plain print about
1console.log('Null Inner');
2 console.log(this.nullInner.inner.data);

Oops, something went wrong:

You can't access the data property on an undefined error. The old way to fix this is like this:

view plain print about
1if (this.nullInner.inner) {
2 console.log(this.nullInner.inner.data);
3 }

That works, however the use of optional chaining gives us some syntactical sugar when checking for the error:

view plain print about
1console.log(this.nullInner.inner?.data);

Typescript sees the question mark after the inner reference and knows to stop following down the chain if inner is undefined or null.

Let's look at our next object, the one with null inner data. In TypeScript this worked fine w/o issue, even though the property is missing. Here it does too:

view plain print about
1console.log('null Inner Data');
2 console.log(this.nullInnerData.inner.data);

Inner is a real object, and accessing data on it returns undefined.

Finally, output the null instance. This is the one we never defined:

view plain print about
1<h1>null Instance</h1>
2{{nullInstance.inner.data}}

This will show an error:

Since nullInstance is undefined, the inner variable cannot be accessed.

You might fix this like this:

view plain print about
1if (this.nullInstance && this.nullInstance.inner && this.nullInstance.inner.data) {
2 console.log(this.nullInstance.inner.data);
3 }

This is a complicated if statement, and is where optional chaining really Starts to shine:

view plain print about
1console.log(this.nullInstance?.inner?.data);

Rerun the code and see everything error free.

Of course, if you can't drill down to the bottom of the chain, you'll get an undefined returned. But, that is better than a runtime error in your user's browser.

Final Thoughts

Go play with the code here!

My team is really excited about using this one to tweak and simplify our code, especially since some of our business objects have nested structures.