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:
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.
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:
2 this.populatedInstance.inner = new InnerData();
3 this.populatedInstance.inner.data = 'name';
The nullInner variable will not create the Inner value:
Now, let's go ahead and create the null inner data:
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:
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:
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:
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:
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:
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:
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:
2 console.log(this.nullInstance.inner.data);
3 }
This is a complicated if statement, and is where optional chaining really Starts to shine:
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
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.