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…

How Many If Statements do You Really Need to Check for Null? poster

Take control of your career. Build JavaScript mobile apps.

ng atlanta

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. enter image description here


Important things to note:

  1. No try/catch solutions (try/catch should only be used for things which are out of your control)
  2. There is full intellisense and typing recognition
  3. Support for arrays

This function can be used in TypeScript, JavaScript, and of course NativeScript.


Until the next article, Take care :)


Royi lives in Israel. He's a Stackoverflow addict, full stack developer and internals enthusiast.

Did you enjoy this? Share it!

Take control of your career. Build JavaScript mobile apps.