TypeScript
How to Detect Null and Undefined in Your TypeScript Code
Checking for null and undefined is a common task in TypeScript (and JavaScript in general). There are a few ways to do it, so in this article we’ll explore some of the most common techniques for checking null and undefined values in TypeScript.
Equality Operator.
The most straightforward way of checking is of course the equality operator.
const myValue = maybeNullOrUndefined();
if (myValue === null) console.log('Null!');
if (myValue === undefined) console.log('Undefined!');
I've used the strict equality operator here, "===". That's as opposed to the less strict regular equality operator "==". What's the difference? The strict equality operator cares about types, whereas the regular equality operator will try to convert values to the same type.
So for the strict equality operator, "null !== undefined", but the regular equality operator will consider them the same.
null == null; //True
undefined == undefined; //True
null == undefined; //True
Whether this matters depends on your code - I tend to stick to the strict equality operator, since I would prefer to write my code in a way where I know to expect null or undefined.
Conditionals.
For conditionals, null/undefined are considered falsy. This means that you can shorten the above code:
const myValue = maybeNullOrUndefined();
if (!myValue) console.log('Null!');
if (!myValue) console.log('Undefined!');
The key here though is that anything falsy will trigger this, including the number 0 and the empty string:
const myValue = ''; //maybeNullOrUndefined();
if (!myValue) console.log('Null!'); //Prints
if (!myValue) console.log('Undefined!'); //Prints
Nullish Coalescing Operator.
Another way to check for null or undefined is to use the nullish coalescing operator (??), which was introduced in TypeScript 3.7.
If the left-hand side of the operation is non-null it returns that, otherwise it returns the right-hand side otherwise.
Here's an example:
const foo = null ?? 'Foo';
const bar = undefined ?? 'Bar';
const baz = 'Baz' ?? 'Qux';
console.log(foo, bar, baz);
- The first ?? picks 'Foo', since the left side is null
- The second picks 'Bar', since the left side is undefined
- The third picks 'Baz', since it isn't null or undefined
The operator uses short-circuit evaluation, meaning if the left-hand side is null or undefined, the right-hand side will never be evaluated, e.g. if it's a function, it will never be called:
function printIfCalled(value: string) {
console.log('Function Called:', value);
return value;
}
const a = printIfCalled('a') ?? printIfCalled('b');
Here the left-hand side gets evaluated, but since it isn't null, the right-hand side never gets evaluated.
You might see this most commonly used for giving default values to variables, especially environment variables:
const BASE_URL = process.env.BASE_URL ?? 'http://localhost:1738';
Nullish-Coalescing Assignment Operator
You might see it less often, but there is also a nullish-coalescing assignment operator. It looks like this:
let foo = null;
foo ??= 'foo';
foo ??= 'bar';
console.log(foo); // 'foo'
It's just nullish-coalescing and assignment combined - if the value on the right is non-null, it will be assigned to the value on the left.
Optional Chaining.
TypeScript also provides a useful shortcut for checking if a property on an object might be null or undefined.
Let's make up an example with a mock shopping cart:
type Cart = {
items: string[];
clearItems: () => void;
};
function getCart(): Cart | null;
Our "getCart()" function might return null. That means we can't do this:
myCart.clearItems(); //'myCart' is possibly 'null'.ts(18047)
So we could use one of the previous methods to check for null or undefined:
if (myCart) myCart.clearItems();
Or use optional chaining:
myCart?.clearItems();
It's called optional chaining since we can chain as many of these together as we like, through as many keys that might not exist.
type Cart = {
items: string[];
clearItems: () => void;
subCart?: Cart;
};
function getCart(): Cart | null;
const myCart = getCart();
myCart?.subCart?.subCart?.subCart?.clearItems();
If we go up the chain and reach something that isn't defined, the whole thing short circuits and we just get undefined.
You can also use it for functions. Let's change our "clearItems()" function to maybe be undefined:
type Cart = {
items: string[];
clearItems?: () => void;
subCart?: Cart;
};
function getCart(): Cart | null;
const myCart = getCart();
myCart?.clearItems?.()
Conclusion.
Thanks for reading! Remember to choose the technique that fits your code best, and bear in mind which methods are stricter than others. If you’re interested in further reading, why not read more about TypeScript's primitive types., and if you just liked the article, follow me on Twitter.
read more.
I'm a full-stack developer from the UK. I'm currently looking for graduate and freelance software engineering roles, so if you liked this article, feel free to reach out.