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
undefinedfor initially undefined values and usingnullwhenever you want to unassign a value a good option? - Using
undefinedeverywhere simplifies things at first, but then there is JSON (onlynullavailable) 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@Inputsyou 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?
Latest comments (16)
I think null is for explicitly acknowledging that this has no value and if it's undefined it's a better indication of a bug / error / unhandled case.
A simple rule that I introduced in the previous company where I worked:
Always initialize variables with
null, any encounteredundefinedwas treated as an error.Typically, we'd only have
undefinedvalues when working with externally provided data, and we'd have validation logic on incoming data anyway, which took care of transforming it intonull(or throwing an error, depending on which was appropriate).Simply put, a
nullis intentionally empty, anundefinedindicated that something wasn't 100% right.I use
undefinedis a abort in binding, for example in Web Atoms framework, we can create binding likeIn above case, this basically refreshes text whenever
aborcis modified, however, I can set any of it toundefinedto cancel the update operation. Update will occur only if all are notundefined, this allow me to clearly distinguish betweenundefinedandnull.You don't deal with
nullvsundefinedbut you use it as one thing. The key is to use the concept ofnullish. My few cents here - Maybe Just NullableWhen 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...
Simple answer: Be consistent with their use in your code.
There are subtle differences, were one can have variable behavior from the other. However, in most cases it is personal preference.
There 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.
nullis 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.undefinednever 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
nullhow it was designed, but I already make complex enough code, KISS has to be my mantra for this sort of thing.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).