Written by Marcin Kamiński
Published May 26, 2022

JavaScript Dilemma: null or undefined

Intro

Since I work with JavaScript, I have often found myself in a position where I was discussing with my colleagues if we should use null or undefined. I’ve spent a lot of my time explaining my point of view and justifying my choices but as always in JS there are many aspects to consider and many ways to achieve the result. In the endless number of comments I made in the PRs that I was reviewing…people had their own justification for choosing one way or another and there’s no one source of truth.

It’s noteworthy that both null and undefined could work in your project if you are using them in a smart way. Moreover, your team could prefer to use one of those and oppose switching or maybe they don’t even care.

In this article, I will try to explain to you my take on this (crucial) matter. This topic is something that really bothers me very often and maybe you’re in the same situation as well since, after all, you’re here. Let’s dive into it!

Consistency

The first and the most important aspect to me is consistency. No matter if we go with null or undefined, it’s always good to be consistent in your code. Many times, I’ve seen the code that had both of those spread all over the project and I really found it hard to know why. Most of the time, there was actually no real reason other than having many people with different approaches working together on one application.

Pros

  • A consistent code base that’s easier to understand
  • More predictable behavior of the system
  • Lower entry threshold in the project

Cons

  • None (?)

It’s also quite easy to force consistency in that aspect with all the modern tools that are available right now. For example, we could configure our ESLint rules to yell at us when we try to use null over undefined or the other way around. For that purpose, there are already pre-configured solutions available for ESLint like no-undefined or no-null rules. By using one of those, you could make your code consistent and you won’t need to expect your colleagues to REMEMBER (or simply know) what they should use, as that never works.

Amount of code

When it comes to JavaScript, there’s a clear winner – undefined. By using undefined, most of the time we can avoid additional lines of code and characters that we have to type. That, of course, depends on the specific case (as always), but from my experience in JS it turns out that it was not necessary to overuse null. Let’s consider some examples here.

Example 1 – declare, modify, return

The first example shows how you could declare some variable that you modify and return at the end of the block. In this case, of course, you could use the arrow function and implicit return but this is just to showcase the usage and difference between using null and undefined in a similar case.

Null

let result = null;

if(something === 0){
    result = 1;

}

return result

Undefined:

let result;

if(something === 0){ 
    result = 1;
}

return result;

Therefore, here, it’s not too much of a difference, just one word. We could argue about whether using undefined over null is really giving us an advantage, but still – it’s “less” code. What is also worth noting here is that we have only one variable and a super simple code example. You can imagine how it looks when you have more than 10 variables and many lines of complex code. Still not convinced? Let’s move forward.

Example 2 – TypeScript

When we’re using TypeScript in our projects and we want to type everything properly, the consistency that was mentioned earlier is really important. So let’s look into the example of how using null and undefined looks in TypeScript.

Null

interface SomeType {
    first: number | null;
    second: AnotherType | null;
    okThatsLonger: string | number | boolean | null;
}

Undefined:

interface SomeType {
    first?: number;
    second?: AnotherType;
    okThatsLonger?: string | number | boolean;
}

In this example, we could make use of the “optional” functionality that TypeScript provides and replace all the “| null” with “?”. The more types you create, the more nulls you have to type. Moreover, TypesScript has the utility type Partial<T> that allows for making all of the properties in a specific type optional and that provides another advantage to undefined. In the null case, we have to most probably rewrite the type, since there’s no utility function in TypeScript to do that job for us, or we could create our own. Also if we look into functions declaration and inline types, it makes a difference.

Null

const foo = (x: number | string | null) => { ... }

Undefined:

const foo = (x?: number | string) => { ... }

It doesn’t take too much effort to add a null, but let’s imagine that we have to do it all over our huge codebase. When we are scraping some proof of concept and we want it to be ready really quickly, it takes time to add it and there’s a chance we can make a typo.

Example 3 – Optional chaining

Straight from the TypeScript, we move on to the optional chaining. It’s still quite new in JavaScript and some people forget that it exists. Here, we can really see how much of a difference it makes to use undefined over null. Especially when we combine it with arrow functions and implicit return. Let’s look into that.

Null

const foo = (x) => x?.first?.item?.title || null;

Undefined:

const foo = (x) => x?.first?.item?.title;

So the first one adds some abstraction that is most probably driven by the consistency reasons or just because we like null. Let’s add some TypeScript on top of that.

Null

const foo = (x: XType | null): string | null => x.first.item.title || null;

Undefined:

const foo = (x?: XType): string | undefined => x?.first?.item?.title;

To be honest, that’s the example that’s most of the time showing overuse of both null and undefined. Depending on a use case, of course, I would love to return an empty string instead of either of those mentioned. It definitely takes a longer discussion to consider everything in here but most probably later on you could either check if it’s empty or simply return it, which will also work.

const foo = (x?: XType): string => x?.first?.item?.title || ‘’;

The reason why I’m mentioning this is that in the next example we will consider…

Null

JSON.stringify({ x: null })

Undefined:

JSON.stringify({ x: undefined })

External systems

Yes, external systems. That could really be a pain for undefined lovers (or not). Depending on the parsers that external services use, translators, types etc., of course, it could happen that when we send undefined (meaning that the variable doesn’t exist at all) to the Java-based system (that doesn’t have undefined – only nulls!) it will crash when with null everything could be fine. As I’ve mentioned, it depends on how the external service is created, if types/interfaces match and if there’s any layer that could translate the objects passed between them. This issue can also be quite easily avoided. Therefore, I would say it’s not really an argument for using null instead of undefined but rather something that we will need to have in mind. We can quite easily check how it looks.

That would be it for the examples. Some of them are more or less adequate and could be an argument to use one solution over another. However, there are still more things to consider.

JSON Data

As mentioned before in example 4 (“External systems”), choosing undefined over null could cause some problems when we deal with external systems that are written in languages that don’t support undefined. Nevertheless, if we work with the API that’s, for example, written with Node.js, it’s a different story.

When we send over the requests between different services, our data gets serialized (most often) to JSON. As shown in example 4, null will persist in the object in that case when the undefined will be actually erased from it. That could be an advantage. Considering that Node.js API is properly written and knows what it’s expecting, undefined is not an issue anymore but reduces the size of the data that we send over. To compare – simple JSON object filled with data like this:

{
    "First": "something,
    "0": null,
    "1": null,
    ...,
    "1000000": null
}

Had the size of 13.9 MB when the same data structure, filled with undefined, only had 21 bytes because the only property that was left was the “first” one.

Moreover, by using undefined in this case, we can improve the readability of the JSON data. In a large data structure, removing all the fields that are null can make it clearer and smaller at the same time. So as long as the service that we are sending the data to doesn’t have any problems with parsing it, there’s a lot we could gain from it.

Mainstream

Last but not least, let’s look at the market and how bigger players are dealing with it. Many companies have different approaches of course but from my research, it turns out that there are definitely more examples of preferring undefined over null than the other way around.

TypeScript (Microsoft)

In TypeScript’s coding guidelines, we can find the information that we should not use null but use undefined instead. Since TypeScript is widely used and Microsoft is a very successful company, we could consider it as some kind of reference.

Unicorn ESLint Plugin

Popular ESLint Plugin called Unicorn, with 2,500 stars on GitHub introduces the rule called no-null in their recommended config. Personally, I really like this plugin for many reasons and so do almost 3,000 people that starred the project. It’s also noteworthy that the plugin was installed in almost 1.2 million projects where (maybe) people used default configuration with a no-null rule enabled.

Billion-dollar mistake

Many articles mention null as a “billion-dollar mistake”. Long story short – myths state that null was never supposed to be included in JavaScript, but since it was back in the day, removing it later could cause plenty of problems and that’s why it was never taken down. There were also many debates on the forum around the topic. For example, there was a long conversation on the typescript-eslint project on GitHub on one of the issues about this dilemma.

Summary

As always in JS, as I always mention, in the end, it’s up to your own taste regarding what you go with. I would say that the most important thing is consistency in using one of those and not using them unintentionally in different places for different purposes. In addition, sometimes there’s a case that we have to use one of those or maybe we have to meet some requirements but still it can be achieved in many (more or less) clear ways.

Of course, there are always some bigger reasons why our projects look that way and not another way. I tried to throw some light on this topic that is triggering so many people so everyone could decide on their own as to what they prefer.

Hopefully, you enjoyed this article. Let us know what you think about it and what you are going to use in your next project – null or undefined? Or both?

Written by Marcin Kamiński
Published May 26, 2022