-
Notifications
You must be signed in to change notification settings - Fork 4
null vs undefined #71
Description
I would like to start a discussion about null
and undefined
. In the end I would like to use only one value to represent a non-value.
Motivation
In my experience it's problematic when developers start to use both to represent the absence of a value. When a value can either be null
or undefined
or something else, people start to write code like this:
if (someValue) {
This is problematic because it may introduce bugs where someValue
is casted implicitly to false
(e.g. with 0
or ""
). The alternative would be to use:
if (someValue == null) {
which conflicts with our rule to use strict equality checks. It's also not intuitive that == null
will also check for undefined
. The biggest problem to me is that == null
is also not optimizable for the browser because document.all == null
might also evaluate to true
(this is a little known fact).
The last alternative would be to write:
if (someValue === undefined || someValue === null)
which is just annoying.
But there are more problems. JS default parameters for functions and destructuring will only work with undefined
. This makes it even more complicated if the codebase is mixing undefined
and null
. On the other hand, JSON.stringify
will just omit undefined
values.
A codebase cannot ignore these issues in my experience. It should stick to a guideline.
Comparison
Pro undefined
- Function default parameters only work with
undefined
- Destructuring with default values only work with
undefined
typeof "object"
works as intended- Arithmetic with non-existent values produces expected results:
2 + null; // 2
2 + undefined; // NaN
Pro null
- JSONs can only contain
null
- React components can only return
null
for rendering nothing (undefined
is not supported) - DOM APIs often use
null
- Functions can distinguish between implicit
return
andreturn null
(although it's not a good style to mix both in the same function) - Distinguishing between a non-existent object property and a null property is straightforward:
obj.prop === undefined
means it doesn't existobj.prop === null
means it exists but it's not set
My conclusion
For a long time (especially pre-ES2015) I was pro null
because setting something to null
explicitly was a clear sign that "there is something, it's just not set". It maps clearly to the concept of known knowns, known unknowns (null
) and unknown unknowns (undefined
).
However, with the rise of ES2015 and the widespread use of default parameters and values, the usability problems became more apparent. In one of our projects we had to do something like this just to use default props:
<SomeComponent
a={model.a === null ? undefined : model.a}
b={model.b === null ? undefined : model.b}
c={model.c === null ? undefined : model.c}
/>
Since this is too much code to write and read, people will either deviate from the rule to use null
and use undefined
where it's easier for them (eventually leading to the situation I mentioned at the beginning) or use unsafe boolean expressions like:
<SomeComponent
a={model.a || undefined}
b={model.b || undefined}
c={model.c || undefined}
/>
Nullish Coalescing will make that less error prone, but it's still unnecessary code in my opinion.
So my proposal would be to ban null
and only use undefined
. The eslint-plugin-no-null
rule could enforce that.
For situations where developers need to use null
(e.g. when returning nothing from React components) we can still have a single module that exports a null
value:
// eslint-disable-next-line no-null/no-null
const NULL = null;
export default NULL;
For situations where developers need to save undefined
to a JSON it's possible to have a utility function that converts all explicit undefined
properties into null
before sending it to JSON.stringify()
. For consistency reasons you would also need the opposite for JSON.parse()
. The null module and these helper functions could be published to NPM so that you don't have to write or copy that again.
I would like to add the no-null/no-null
rule as a style rule to our ESLint config. This means that we can try it out in a project and make some experience with it.
Side notes
Defining optional properties with undefined
is easier in TypeScript:
type A = {
a?: number;
};
vs
type A = {
a: number | null;
};
What do you think?
Other opinions:
- Brendan Eich: "Use undefined"
- undefined vs null
- https://medium.com/@hbarcelos/why-i-banned-null-from-my-js-code-and-why-you-should-too-13df90323cfa
- The TypeScript team doesn't use null
- Douglas Crockford thinks null is a bad idea (not that I would care a lot about his opinions ^^)
- Stop setting your variables to undefined
- undefined vs null