Looking For Properties On Null and Uncaught Errors In Asynchronous Code

I see a common mistake among students learning JavaScript when they try to write a conditional to test the value of an object’s property but the object itself is null. Instead of testing the condition of the property, the expression throws an error.


Conditional testing for a property on NULL

The assumption is made is that the condition will evaluate as false instead of throwing an error.

1
2
3
4
5
6
const foo = null;
if (foo.bar) {
console.log('if');
} else {
console.log('else');
}

No console logs happen. The following error is thrown:
Uncaught TypeError: Cannot read property 'bar' of null


Testing with NOT operator doesn’t work either

1
2
3
4
const foo = null;
if (!foo.bar) {
console.log('if');
}

No console logs happen. The same error is thrown:
Uncaught TypeError: Cannot read property 'bar' of null


Troubleshooting the problematic code above seems clear enough. The error message is useful, it tells us the cause of the problem. What we also see proven true is the following disclaimer about thrown errors…

From MDN:

Execution of the current function will stop (the statements after throw won’t be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.

Uncaught errors will terminate the program.

The correct way to use a conditional for testing for the property of an object that could be NULL is to include a test for the object as well.

Use Logical AND, progress from parent to child

1
2
3
4
5
6
const foo = null;
if (foo && foo.bar){
console.log('if');
}else{
console.log('else');
}

Successfully logs else;


Or guard against the NULL case by using Logical OR

1
2
3
4
const foo = null;
if (!foo || !foo.bar){
console.log('if');
}

Successfully logs if;


Depending on the scenario, another solution is to catch the error.

Using TRY & CATCH

1
2
3
4
5
6
const foo = null;
try {
console.log(foo.bar);
} catch(err) {
console.log('catch');
}

Successfully logs catch
NOTE: even if you don’t use it, the err variable as the parameter for catch() is NOT optional!


An error that doesn’t appear to stop the execution of code

I think the nature of the problem gets concealed when promises come into play. Instead of an error showing up in the browser, the error will go to the catch block of a promise.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const promise = new Promise( (resolve, reject) => {
console.log('inside the promise');
resolve('promise result');
});
promise
.then( result => {
console.log('inside .then() with: ', result);
const foo = null;
if (foo.bar) {
console.log('if');
} else {
console.log('else');
}
// Code after the offending IF doesn't execute
})
.catch( reject => {
console.log('code inside the catch executes');
});

inside the promise
inside .then() with: promise result
code inside the catch executes
Notice that the functions in both .then() and .catch() were invoked. There are no uncaught errors so subsequent code will continue to execute as normal.


Or if a catch block doesn’t exist, an uncaught error is thrown but code will appear to execute after the conditional, potentially hiding the cause of the error.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise = new Promise( (resolve, reject) => {
console.log('inside the promise');
resolve('promise result');
});
promise
.then( result => {
console.log('inside .then() with: ', result);
const foo = null;
if (foo.bar) {
console.log('if');
} else {
console.log('else');
}
// Code after the offending IF doesn't execute
})
console.log('outside the promise');

inside the promise
outside the promise
inside .then() with: promise result
After the logs, the following error is appears in the console:
Uncaught (in promise) TypeError: Cannot read property 'bar' of null

It’s the same error we saw before and it stops the execution of code inside the .then() function, but since it was asynchronous it doesn’t stop the execution of code below the promise invocation. If there is a lot of code following the promise and if the promise is an Ajax call (for example) that takes a sizable amount of time before invoking the .then() function, then the offending error won’t be as easy to track down. If I saw this error message I would search for “bar” and then look at any promises missing catches.