How Many If Statements do You Really Need to Check for Null?
JavaScript developers have a dirty secret: how we handle null and undefined variables in nested objects. It's no secret that as JavaScript…
Take control of your career. Build JavaScript mobile apps.
Catch Dave Coffin, Nathan Walker, and Alex Ziskind at ngAtlanta in February 2020 for an advanced NativeScript with Angular workshop called Breathe life into mobile UX with solid architecture lessons
. You can register now and take your NativeScript skills up a notch. Register here.
JavaScript developers have a dirty secret: how we handle null and undefined variables in nested objects. It's no secret that as JavaScript developers we work with objects that often contain other nested objects and even arrays and primitive types, but it's how we handle nulls that's sometimes shameful. While in a browser, a null reference will just show you a little red circle in Developer Tools, in a NativeScript app, things might be a bit more problematic.
Today I want to show you a trick to handle a scenario that we all encounter along our dev journey; treating nulls in TypeScript and JavaScript, which applies to NativeScript apps.
The Problem
What happens if one of the nested objects is null
or undefined
, but you don't know which one?
You might ask - "So? Where is the problem?".
The problem begins if you try to access a property of null
or undefined
. This is where things get complicated. Other lnaguages recently got updated to handle these scenarios in elegant ways. For example, C# has solved this nicely using the Null Conditional.
Let's say we have this C# code
class Student
{
public Address myAddress;
...
}
Where Address
itself is
class Address
{
public HomeAddress myHomeAddress;
...
}
It is perfectly a valid task to access myHomeAddress
like this:
var student = new Student();
var myHomeAddress = student.myAddress.myHomeAddress;
But what if myAddress
or even student
is null?
We will get a runtime error called NullReferenceException
.
Sure we can do this to check for null using conditional statements (aka if
statements)
if (student != null && student.myAddress != null )
{
Console.Write(student.myAddress.myHomeAddress ?? "n/a");
}
Now, imagine what happens if we have a complex object. Imagine how many if
's we'll have!
How C# Solved This
Here is how C# solved this problem by adding the Null Conditional operator to the language.
Console.Write(student?.myAddress?.myHomeAddress ?? "n/a");
Nice right? Ok, Enough with C#, let's talk about TypeScript.
Unfortunately, TypeScript doesn't have this operator yet and still waiting for ECMAScript to standardise it.
Note: See this TypeScript issue on GitHub for more info.
A Solution in TypeScript
Say I have this object:
var o = {
a: {
b: {
c: [
{
k: 1
},
{
k: 2
},
{
k: 3
}
]
}
}
};
And let's say I want to access the second element in the array (which is k
). Normally I'll have to do this
if (o && o.a && o.a.b && o.a.b.c && o.a.b.c[1]) {
doSomethingWith(o.a.b.c[1].k);
}
Pretty ugly, isn't it? Again these are just a bunch of if
statements, disguised with syntactic sugar.
In C# this would be
doSomethingWith(o?.a?.b?.c[1]?.k);
Never fear! I've build a function that can help us with this problem. Here it is:
function n<T>(o: T, action: (a: T) => any) {
let s = action
.toString()
.split('return')[1]
.split('}')[0]
.split(';')[0];
s = s.replace(/\[(\w+)\]/g, '.$1');
s = s.replace(/^\./, '');
var a = s.split('.');
for (var i = 1, n = a.length; i < n; ++i) {
var k = a[i];
if (o.hasOwnProperty(k)) {
o = o[k];
} else {
return null;
}
}
return o;
}
This function will allow us to call it like this
alert(n(o, k => k.a.b.c[1]));
You can play with a live demo of this in the TypeScript playground that I put together.
Here is a screenshot of this running in the TypeScript playgound, and I want you to notice a few things here.
Important things to note:
- No try/catch solutions (try/catch should only be used for things which are out of your control)
- There is full intellisense and typing recognition
- Support for arrays
This function can be used in TypeScript, JavaScript, and of course NativeScript.
Until the next article, Take care :)