Should you prefer Null or Undefined in JavaScript?
The internet contains lots of tutorials describing the differences between null
and undefined
.
But most of them fail to see that this isn’t a feature, but rather a bug in the language.
Note: Throughout this article I'll be using the term “null” in normal font
when referring to any null-like value, whatever it happens to be called.
I'll be using monospace font when speaking specifically about JavaScript values
null
and undefined
.
While many languages have copied the billion-dollar mistake of Tony Hoare
by introducing a null values in the first place,
JavaScript author Brendan Eich managed to double the mistake by introducing two null values:
null
and undefined
.
The original goal of null
being better interop with Java.
Like many Java-driven changes in JavaScript, this made the language worse.
Luckily nowadays we can use TypeScript to track which values can be nullable.
This eliminates the need for null-checks where they’re not needed and
lets the type-checker remind us if we have forgotten to add a null-check.
But because null !== undefined
, you should really just pick one of these
null-values and eliminate the other from your codebase.
But which one to choose?
Advantages of undefined
The main advantage of undefined
is that it’s deeply integrated with the JavaScript language.
Lots of language constructs will provide you with undefined
by default:
- Unassigned variables have value
undefined
. - All functions without explicit return value return
undefined
. - Optional function arguments default to
undefined
. - Accessing missing fields in an object or array will result in
undefined
. - The optional chaining operator
foo?.bar?.baz
will returnundefined
when part of the chain is missing. typeof undefined === "undefined"
while confusinglytypeof null === "object"
- TypeScript has special syntax for working with
undefined
(e.g.{age?: number}
), but there’s not such syntax fornull
(you’d have to use{age: number | null}
). - Most standard-library functions return
undefined
to indicate a missing or no value.
Advantages of null
- When serialized as JSON, fields with
null
values will be preserved (e.g.{"foo": null, "bar": 1}
), whileundefined
values will simply be skipped (e.g.{"bar": 1}
). - Historically
undefined
was not directly accessible, whilenull
was an actual keyword. - Some libraries require the use of
null
. Notably React has usednull
to render empty components, though nowadays you can instead use empty fragment<></>
. - Some standard-library functions return
null
. Notable examples:RegExp.exec()
anddocument.getElementById()
.
To me, all these advantages of null
seem rather questionable when compared
with the pretty clear advantages of undefined
.
My personal experience
I have adopted a no-null
policy in most of my personal open-source projects
and I’ve also managed to adopt this style in some commercial projects I’ve been
involved with. I even wrote an ESLint no-null plugin which notifies
you about nulls in your codebase.
I haven’t really run into any problems with this.
There definitely are times where you do need to use null
here or there.
Usually because some external library requires it or because the backend API
you’re communicating with contains null
values in JSON, but both of these
cases can be easily remedied by implementing helpers to convert these
null
values to undefined
, so you’ll only need to deal with null
at the
edges of your system, and the rest of it can be free of null
.
For example, here’s a TypeScript function I’ve used
to replace occasional null
values with undefined
:
function nullToUndefined<T>(value: T | null): T | undefined {
return value === null ? undefined : value;
}
And here’s a version JSON.parse()
that will abolish null
values:
function parseJson<T>(json: string): T {
return JSON.parse(json, (key, value) => nullToUndefined(value));
}
In general, it has been a smooth sailing.
This is in great contrast to one commercial softwere project I was involved with,
which had adopted the rule to prefer null
over undefined
.
As a result, there was a mix of null
and undefined
used all over the codebase,
with many places using both simultaneously (as demonstrated by TypeScript
declarations in the form of age?: number | null
).
Honestly, I think the codebase would have looked better
if no policy towards null
and undefined
had been adopted.
This experience confirmed my belief that it’s really hard to avoid using undefined
in JavaScript, as you’ll be fighting against what the language naturally prefers.
Don’t make the same mistake, and just use undefined
instead.