null vs. undefined: Can I use only one?

I tried it on my React app

OOO
6 min readMar 29, 2022
Photo by Ben Hershey on Unsplash

Why do you wanna use only one in the first place?

null and undefined are used interchangeably to represent non-value. Why can’t I just stick with one?

At first, I used both because they have different definition

Here’s the definition from TC39:

From a non-value perspective, I redefined them as:

  • undefined — a non-value that you don’t explicitly assign.
  • null — a non-value that you explicitly assign.

With above context, if I were to create an optional selectedDate state that can have type of undefined, Date, and null, each would mean:

  • undefined — user haven’t selected date yet
  • Date — user selected date
  • null — user cleared selected date

In code:

In above example, selectedDate having both type of undefined and null as non-value allows it to be distinguished whether it’s been touched by the user or not. You may want to allow submit button to activate only if user explicitly sets the selected date. It’ll be as simple as isDisabled={selectedDate === undefined}.

But often times, there is no need to handle null and undefined separately. That means for above example, all I want to know is that whether there’s selected date or not:

Having both undefined and null are just adding more noise.

Then I started to wonder,

“Can I just use only one non-value?”

In fact, many other programming languages only have one non-value representation and they have no issue with it. Searching null vs. undefined in Google proves that having two non-values in Javascript is painful. It is even considered as design mistake by JavaScript’s creator, Brendan Eich.

So I wanted to simplify things by having only one non-value.

Using only null

The biggest downside of only using null is that undefined is everywhere.

Again, a variable is undefined if it isn’t assigned to any value. And to prevent this, I would then have to assign null on init:

let iDontKnowTheValueYet: null | string = null;

Using only null gets more annoying in React w/ Typescript.

We often define props when creating component:

And when you want to pass an optional prop, a common way is to use ?:

But this is a shorthand of optionProp: string | undefined. So if we’re making it string | null, then another problem occurs:

And there’re more but I believe above cases already justifies why sticking with null is a bad idea.

Then… can I only use undefined?

Beforehand, there’s another question to answer:

Can I assign undefined to a variable in the first place?

It wasn’t safe to do so in the past but doesn’t matter in modern browsers.

In older browsers, it was possible to overwrite undefined because it is not a reserved word unlike null. So it leads to side effect if you do this:

But in modern browsers (JavaScript 1.8.5 / Firefox 4+), undefined is a non-configurable, non-writable property, per the ECMAScript 5 specification.

So it’s no longer a problem if you’re using “modern browser”.

And there’re already some guys doing it: Typescript team only uses undefined and Douglas Crockford stopped using null.

So it’s worth a try.

But it didn’t last long

It’s because there are already too many APIs that produce null for me to avoid. A lot of third party libraries required me to use null.

Here, I’ll only discuss 2 essential libraries that forced me to use null:

Firestore (of Firebase)

When querying documents by optional field

In Firestore, there’re two ways of representing optional field:

  1. Union of null

2. No field at all

Since I was against null, I went with #2.

But the issue with the latter is that you won’t be able to query documents “without” specific field (See the comment of Frank van Puffelen). While for null, you can just do query.where(‘address’ == null).

So in above example, if you want to query all users with no address, you would have to query all users then filter them in client side. The huge downside with this approach is that Firestore charges by the number of documents you read. In short, you’ll pay more and write more code if you stick with no-null movement.

Updating an optional field

There’re two ways of updating document in Firestore:

  1. By using set operation
  2. By using update operation

While set operation requires you to provide all document fields to update, update operation only requires you to provide fields you want to update.

Using update operation is convenient because you don’t always have to hold the whole document in order to update few fields.

But this brings confusion.

The confusing part occurs when undefined is passed to update an optional field. Do you mean to clear the field or to preserve the existing value?

This question often occurs when designing API of a service that handles Firestore’s request and response. For the request payload, making all update-able fields optional makes sense for required fields, but ambiguous for optional fields:

As a solution, you can wrap with a generic type that determines whether to clear or preserve:

But this is ugly, isn’t it? How about just using null to determine clear operation? If you decided to use null to represent optional field in the previous problem, then using null here would make more sense:

React

Using useRef on element’s ref property requires null

This becomes an issue if you’re using Typescript.

If you try to assign reference with value of undefined to an element’s ref, it throws a type error:

But if you pass null as useRef’s initial value, the error goes away.

This is because there’re two roles in useRef:

  1. Role as a value container
  2. Role as an element reference

When you are using useRef as a value container, you can change its value by modifying its .current property. Therefore its type is MutableRefObject<T>, and as its name states, you can mutate its .current value.

When you are using useRef as an element reference, you can only read values from it and React itself is the only one allowed to do writes. Therefore its type is RefObject<T> and you cannot mutate its .current value.

And the problem occurs when you create useRef with an initial value of undefined. If you create useRef with undefined, Typescript assumes that it’s a value container and returns type of MutableRefObject<T>. This doesn’t match the type that element’s ref property is expecting and throws error.

Typescript returns RefObject<T> only if useRef is initialized with null. To find out more why it is designed this way, read this and this.

So you cannot get away from null if you’re using Typescript and has to use useRef as an element reference.

You cannot return undefined in your React component

Although React doc says false, null, undefined, and true are valid children. They simply don’t render.”, returning undefined from a component throws following error:

Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.

It clearly states that if want to render nothing, return null. If not, you have to see that error. No workaround. Return null.

But good news is that React 18 will allow rendering undefined instead of throwing error. So starting React 18, you can opt to return undefined when you want to return nothing.

Conclusion

Not using null worked for Typescript team because none of their dependencies produced null. And I guess Douglas Crockford may have to reconsider using null again if he were to create a React app 😉.

But even with this experience, I still find it convenient to only use undefined as non-value. It is simply because often times, I don’t need to use two non-values. Above exceptions rarely happen and are limited to number of libraries so I didn’t have to care much if I were to not add more libraries into my project.

So in conclusion, I follow this guideline:

  1. In general, use undefined over null to represent non-value.
  2. Only use null if it can’t be avoided or brings more benefit than using undefined.

It was awkward at first because the guideline doesn’t strictly follow the definition of null and undefined, e.g. assigning undefined to a variable. But once I get used to it, I find no meaning on being obsessed with the definition since it simply brings more benefit.

I would like to know if you had similar experience and how you ended up dealing with null and undefined 😃

Thanks for reading!

--

--