I just recently started refactoring my large scale angular app to Typescript's strict mode. Dealing with lots of null checks the question arose again if I should prefer using undefined
over null
or vice versa. A couple of thoughts:
- Is using
undefined
for initially undefined values and usingnull
whenever you want to unassign a value a good option? - Using
undefined
everywhere simplifies things at first, but then there is JSON (onlynull
available) and API responses. How to best deal with those? - How to best deal with pre-checks for when you are reasonably confident that a value is not
null
, but it theoretically could be (e.g. angular@Inputs
you always assign)? Do you prefer to typecast or do you use error checks?
I wonder how do you deal with the problem. Do you prefer one over the other? Do you use both? What's your take on it?
Top comments (17)
I think the complexity people see comes from a lose definition of those two (
undefined
andnull
). They do not represent the same thing. An empty array is different from an undefined array, and it is also different from an array with a null value in one position. In those cases, those definitions get more clear. And I think the best approach is just to use what the values mean.So, I would use
undefined
whenever a variable has not been assigned yet, or when a method or function does not return anything (exactly how the language behaves on runtime).On the other hand,
null
is used when you explicitly say that variable points to no value. You know that variable exists, but you have nothing to assign to it.I would say
undefined
is more a "source code" thing, andnull
more of a runtime thing.Angular manages state in a very complex and imperative way, in my opinion, which makes checking values harder. But I would use
undefined
for undefined values andnull
when I've passed the point where I would assign a value and I had nothing. If it is a collection, I would just initialize it without values.But I don't think there is a silver bullet answer. Each case should be dealt diferently.
Thank you very much! That's an excellent answer and I agree pretty much with everything you say.
I think much of my particular problems are caused by my data being saved to a document oriented schemaless database (first localStorage and now IndexedDB) and the process of it evolving over time made me lose confidence what's actually in there. Probably makes most sense to solve this problem first and then to revisit the null/undefined problem as you mention.
Adding to this answer, in JS/TS if you have to initialize a collection with empty object, it is often too early. Most JS runtime internally implement different optimization based on kind of the object. If you create an empty array, and later populated it with elements, you get a less optimized array than if you populate it on initialization. Refer to V8 blog for more details.
I just use undefined for everything unless some API forces me to use null. Having to decide when to use one or the other is just an unnecessary complexity. 90% of the people I work with struggle to have the time to concentrate on refactoring their code to the point where it is clean, I think it's too pedantic to make them think about something as minor as this on top of that.
You don't deal with
null
vsundefined
but you use it as one thing. The key is to use the concept ofnullish
. My few cents here - Maybe Just NullableThere was some brief conversation on this very topic as part of a broader discussion with @addyosmani on a recent DevDiscuss episode...
Might help add a tiny bit of color to the conversation.
I just use undefined everywhere, and ruthlessly convert nulls to undefined as they come off an API.
I appreciate the difference. I'd sum it up as a nullable type being shorthand for a set with either zero or one item.
null
is just how we say the set has no entries, sonull
, in my head, has a type and is always used in the context of a nullable type.undefined
never has a type, it's just a void.But, I'm really not a fan of this kind of unneeded technical nuance. There are few cases IME where converting all the nulls to undefineds has broken my code, but having a source of undefineds sneak into a system I'd designed exclusively for nulls sounds like a day I'll never get back.
I get why someone would prefer to use
null
how it was designed, but I already make complex enough code, KISS has to be my mantra for this sort of thing.When we write TypeScript apps, we tend to prefer using neither null nor undefined. We use null objects : let’s say you have a Customer to display. We use a const NoCustomer of same type to underline that there is no customer selected/in state at the moment. Leverage the power of typed languages ;)
Now you can say if(customer != NoCustomer) explicit, no doubt possible.
In JS I don't think in terms of null or undefined, but think in terms of "nil".
I use a utility func similar to Ramda's R.isNil.
And I have a complement func called, isNotNil
Source Code:
gist.github.com/funfunction/0e64a5...
I consider the difference to be one of implicitly nonexistent (undefined) vs. explicitly having no value (null). A newly declared variable that has no initial value, vs. an array element that you've cleared, is a suitably illustrative example of the difference.
As for handling them, I consider casting without checks a strict no-no in strongly typed languages like TypeScript. Don't blindly cast... but DO check to make sure the operation you're attempting is valid if you can, and trap the error if you can't.
That's an excellent point! Thank you very much! How do you usually deal with (unexpectedly) failing type checks in these scenarios? Do you just throw an error?
Yep. I error (or skip if optional).
I guess the biggest consideration is this:
Whether actually doing logic like this is a good idea or not is up to the reader (one might argue it is "confusing"), but default values is the biggest gotcha with
undefined
since defaults fill always override it when passing to functions or when destructuring. In those casesnull
will stay.So essentially every time you want to indicate you couldn't get a value it is safer to indicate that as
null
, becauseundefined
may be overridden by a default value. Personally I like to deal with them as equal by using== null
comparison. And I dislike applying strict typing to JS so I don't need to waste time telling computer what things are. Instead I force type to whatever is needed, such as using~~variable
to forcevariable
as integer Number that is neverNaN
, or check that the type matches expectation. This has the benefit of working in runtime as well (unlike TypeScript which doesn't help you with unexpected runtime issues).I use
undefined
is a abort in binding, for example in Web Atoms framework, we can create binding likeIn above case, this basically refreshes text whenever
a
b
orc
is modified, however, I can set any of it toundefined
to cancel the update operation. Update will occur only if all are notundefined
, this allow me to clearly distinguish betweenundefined
andnull
.