Our code is more robust and legible. But we hide NULL under the rug
TL;DR: Avoid Nulls and undefined. If you avoid them you will never need Optionals.
Problems
Nulls
Solutions
Remove nulls
Deal with undefined
Context
Optional Chaining, Optionals, Coalescence, and many other solutions help us deal with the infamous nulls.
There's no need to use them once our code is mature, robust, and without nulls.
Sample Code
Wrong
const user = {
name: 'Hacker'
};
if (user?.credentials?.notExpired) {
user.login();
}
user.functionDefinedOrNot?.();
// Seems compact but it is hacky and has lots
// of potential NULLs and Undefined
Right
function login() {}
const user = {
name: 'Hacker',
credentials: { expired: false }
};
if (!user.credentials.expired) {
login();
}
// Also compact
// User is a real user or a polymorphic NullUser
// Credentials are always defined.
// Can be an instance of InvalidCredentials
// Assuming we eliminated nulls from our code
if (user.functionDefinedOrNot !== undefined) {
functionDefinedOrNot();
}
// This is also wrong.
// Explicit undefined checks are yet another code smell
Detection
[X] Automatic
This is a Language Feature.
We can detect it and remove it.
Tags
- Null
Conclusion
Many developers feel safe polluting the code with null dealing.
In fact, this is safes than not treating NULLs at all.
Nullish Values, Truthy and Falsy are also code smells.
We need to aim higher and make cleaner code.
The good: remove all nulls from your code
The bad: use optional chaining
The ugly: not treating nulls at all
Relations
Code Smell 145 - Short Circuit Hack
Maxi Contieri ・ Jun 30 '22
Code Smell 69 - Big Bang (JavaScript Ridiculous Castings)
Maxi Contieri ・ May 4 '21
More Info
Null: The Billion dollar mistake
Maxi Contieri ・ Nov 18 '20
How to Get Rid of Annoying IFs Forever
Maxi Contieri ・ Nov 9 '20
Credits
Photo by engin akyurt on Unsplash
He who fights with monsters might take care lest he thereby become a monster. And if you gaze for long into an abyss, the abyss gazes also into you.
Nietzsche
Software Engineering Great Quotes
Maxi Contieri ・ Dec 28 '20
This article is part of the CodeSmell Series.
Top comments (29)
This is your opinion, and is ok ... my problem is mainly that is presented as fact, when it isn't.
You say that it "seems compact", but it IS compact. You also say is "hacky", but...
Your proposed "solutions" are to either take more memory with default values (in the case of the object), or do an unnecessary checks that are already done for you with optional chaining (in the case of the function). You can see how TypeScript handles optional chaining when compiled as ES5 here, but the long story short is that is pretty much the same as what you did with the if, but with less boilerplate.
Now don't get me wrong, I agree with your point about how
nullshould be avoided (I even wrote about that on a post of mine), butundefinedis ok, and both optional chaining and nullish coalescing are great tools to deal with possible missing values. Writing your own "abstraction" of nullish values serves no purpose other than over-engineering and over-complicating a simple language feature.One thing that we need to keep in mind as developers, is that stuff that looks like a "good practice" in a language, doesn't necessarily translate to a "good practice" in another language (and it could even be consider bad). Case in point is that Java is an OOP language with strong use of classes, which I already discussed several times here in DEV are terrible in JavaScript (and I would even say are terrible in general), because they provide a really poor form of encapsulation and reuse, compared with just using functions, modules and composition. So maybe from a class mindset it makes sense to avoid language features such as optional chaining, but it definitely doesn't make sense from a JavaScript point of view, when it actually helps you write less boilerplate to achieve the same goal.
Is almost like when those devs that need to put everything inside classes to the point where if they need to do an addition, they...
But long story short, I would recommend to change the wording of this kind of articles a little to make them look more like "opinion/suggestions", and less like "facts/rules", so we avoid confusing juniors that might think this are like "universal rules" when they aren't.
Cheers!
1 I have no strong evidence it takes more memory.
Memory is expendable. Readability and maintainability aren't
It seems we are dealing with 'Premature Optimization'.
I also think 'undefined' are yet another code smell.
I will also very happy if juniors stop using nulls since we are no longer in the 50s.
But I wil DO take your advice and add a disclaimer these articles are opinions and write it down
I am also reading your articles!
I'm a beginner and am using
nullandundefinedin my current project, and I'd love to hear why they are bad. 🤔For example we have an
Orderobject, and it has a property calledfinishedAtwith typeDate. What should I return if the order is not finished? I'm usingnullin this case.if the order is not finished you shouldnt ask for the date. it makes no sense in real world
An exception should follow fail fast principle
An unfinished order is signified with
.finishedAt = undefined. If you make this property throw an exception, you are violating:dev.to/mcsee/code-smell-73-excepti...
Hi @mrcaidev! You can just omit it. From TypeScript, that
Ordertype would look something like this:So we say
finishedAtis optional (is not always there), that means it can be eitherDateorundefined. Then when you get that from the front-end, you can check iffinishedAtis present, instead of checking if isnull.The best thing is that the response will also be smaller:
The property is only sent if it is set, if not, then its omitted :D
Cheers!
no. an unfinished order has an status unfinished or it is directly unfinished.
Asking for its data is an Exceptional case. Therefore is not an exception for Expected Case
What is your date of death?
How would you answer it?
undefined? null ? 1970-01-01 ?
No. you should say: 'You cannot ask me this. I am still alive!!'
Date of death is an optional property that isn't set unless the status is "dead". You don't throw exceptions for optional properties x.x
Ok. That's your opinion.
I am not going to change it
Maybe write another smell about "optional" properties
How do you represent optional data, then? Do you create new classes for every possible scenario? Like
UserandDeadUserinstead of just having a status property and some optional properties that you read depending on that status?My "opinion" is based on the fact that throwing when accessing an optional property produces a bad DX because now I need to try/catch everything, and also produces a bad UX if we forget about it.
Is really easy to type optional arguments, and IDEs will pick on that and give you feedback if you're trying to read a property that might not be there unless you check or use optional chaining. We can't say the same thing about code that throws. We don't know when something might throw, do we need to do smellier things like wrapping everything in try/catch, using naming conventions to know when someone might throw, etc.
Nowadays folks tend to avoid libraries that throw just because they are unnecessarily problematic, so why would we write code that throws ourselves?
It is not User responsibilty to know if its dead or Alive.
Not everything is an attribute
Code Smell 35 - State as Properties
Maxi Contieri ・ Nov 27 '20 ・ 1 min read
Optional Arguments are 'easy' and 'buggy'. so you type hint but you skip type hinting.
I would not use a library that throws an exception.. unless I am making a mistake
Oh yeah, we are definitely in different boats here. Having a class for the Order status and another class for the Order is definitely what I was talking about when I mentioned over-engineering. Might make sense if you're working with the limitations that classes have, but doesn't make any sense to do that in JavaScript. This is what I was talking about when I said that "bad practices" in one language don't translate to "bad practices" in another. From my PoV, having a class for the status of another class is bonkers (heck, having an external object for the state of another object doesn't make much sense).
Thank you for your advice! I was too particular about keeping a fixed structure of the response data, thinking it would benefit in the long run. Now I know what I should do!