Discussion was locked because I moved away from DEV to blog on my personal website. If you want to chat about the content of any article, hit me u...
The discussion has been locked. New comments can't be added.
Some comments have been hidden by the post's author - find out more
For further actions, you may consider blocking this person and/or reporting abuse
1) Json doesn’t have undefined, also when you interact with other sources (db, cache,..)
2) null and undefined are totally different things, used in different cases.
When the value is null, you know it was defined
When the value is undefined, you know it wasn’t defined
Example, a validator should know if a variable is missing (undefined), or it’s presented without value (null)
Wondering how no one still mentioned 'in' operator:
Several reasons, I guess:
undefinedinvalid, because withinyou can figure out if it was intentionally set asundefined, or is actually missing from the object.null,inwill returntrueas well, so that doesn't make it better thanundefined, nor different. So another argument in favor of "they are the same".in.TL;DR: Because is an argument in favor of the article, I guess I could even update it with that :D
You should explain that to me. I mean you're the ones saying "null means something is defined but intentionally empty", so you're the ones saying there's a need for that kind of thing ... I'm just saying that using
inyou can achieve the same thing withundefined... in both cases we are saying something is "defined but not quite", just with different wording ... and I still don't get why you need that distinction 🤣Well, I tried to find an example but cannot :)
And in this whole long holywar I cannot see a single example why null is better. JSON is one example, but let's imagine we delete all null keys from objects after getting data. Okay now we have libraries which are requiring null, and we have to convert undefined to null when using them. And that's all.
Maybe null make life easier when you are not using TypeScript, I don't know. With TypeScript you can see type in the code, and without it people are probably doing console.log(data) all the time to see data shape, and if values are omitted this is confusing.
Good article, thanks for writing, please keep doing it!
Well not only me, also other people here were trying to explain to you, but seems you just didn’t want to get it.
In my experience, consistency is one of the most important thing. Example: my service exchanges data with other services, they will have the same schema where they agree how an object looks like.
When other service sends me a kebab request with “ketchup” is null, I understand this kebab should be made without ketchup.
If the “ketchup” key is undefined, I understand that this kebab is on hold, because its definition is incomplete.
But if the “ketchup” key is missing? Wtf? Maybe the request was broken in the middle? Maybe the other service updated new schema and changed the key? Maybe the “ketchup” key is deprecated?
And it turned out it was omitted because some guy thought it’s cool to save some characters…
I understand that’s there is always a way to handle it. But ask yourself: Would you like to write inconsistent code with confidence, or you’d prefer monkey patches here and there with thousands of conditions?
Happy Befana btw
You can use TS without using TS, by typing with JSDocs. Try this out in VSCode:
Thanks a lot for that last line, Roman. I greatly appreciate some good vibes in this comment section ❤️
If you really think about it, we can have that same consistency with
undefinedinstead ofnull. At work we have contracts between the API and the client. We translate the type definitions from the back-end in.d.tsfiles we can use from TypeScript and JavaScript. When something is optional in the back-end, the type isType?, notType | nullorType | null | undefined, so we know with confidence that we can send a value orundefined(namely, not send it). Same applies for the things we get of the back-end, if something we fetch could be missing, then the type isType?again, which means it can be undefined, and we deal with that either with default values, optional chaining and all that sweet tech we have to deal with nullish values.Long story short, you have a convention to use
nullandundefined, that convention could use onlyundefinedas well and work ... and this still doesn't negate the fact that you can useundefinedeverywhere, except when you talk with the back-end.I updated the article adding some other folks that have a similar opinion to mine (even a feature request in consideration to update Angular so it only uses
undefined).Offtopic, I was reading your previous article and wondered why to compose functions in this way
update('user')({ ...data })(user), looks like you are intentionally keeping all functions with one argument, and I don't understand why not to have a function with 3 arguments instead. I'd love to read a new article from you "You don't need second argument" explaining this pattern. Looks crazy tbh.Also "You might not need" is a way better naming, because people need classes in Angular, indeed they need mutation in Vue, null in response from database.
Is a pattern called currying, it allows you to create functions from functions and make everything more reusable:
Maybe is not the best example, but basically you can create
updateXand reuse it all you want, with different values for different objects ... compared to doing the same with 3 arguments:We need to write update and pass every argument with every use. Currying is one of those things that when it "clicks", you never go back. A popular library that uses this a lot is Rambda.
I considered that rename a few times, but my point is that you don't need it in your own creations, that's the main reason I have my articles set so that is more for "almost experts" than "beginner". Beginners using other folks tools need to learn all this stuff, way later we learn about the things we don't need to code, but might need to interact with somebody else's code.
Thanks, but still more in depth article would be awesome. By your example I can see that in plain JS carrying is worst thing to happen: if you see somewhere in the code nameFoo, you'll probably assume it's a simple string, while actually it's a function which takes object and returns new object with updated name property. With TypeScript it's still very confusing, okay we can hover and see the type, but I assume it will show a complex barely readable type. And I hardly imagining how it is even possible to write all this types, how much complex generics will be needed for this, how much unions.
In TS function "update" has to take a union of all table names. And what if for one table update should have different implementation?
While in true FP languages there is a function overloading and you can define them separately for different tables, which is removing all downsides and sounds awesome. But JS/TS is not such language so it's a good question if the same approaches really worth to be used here? (I didn't ever worked with FP language)
I assume you are following this carrying approach on real projects and you know how to deal with difficulties on this way, so let me repeat myself, isn't it bright idea for article? There are a lot of articles on the topic ofc, but they only sharing basics and explaining nothing useful about how to deal with it in real world, when to use and when to avoid. Not insisting of course, maybe there are such articles and need to search harder and play with it myself
Sure thing! I'll create an article about currying and mention you on it for pushing the idea if you don't mind ^_^ ... about the TS implementation, that
updatefunction could look something like this:I do love to do TS + FP, so creating an article about this might be interesting for other folks as well 😄
Awesome! No one yet wrote article in my honor, I'm flattered :)
I wish it could explain "Why" and "When to use" no less than "How to use", so pros and cons.
So far carrying looks like a lego and there is something in it, definitely there is something about it.
You don’t need “in” /s
Btw it’s fun when
Yup, it kinda sucks, but is preferable to return
undefinedfor something that isn't defined ... you can still check if a variable exists before trying to access it, by usingtypeof:Accessing properties is a better DX, and with
?.and??is even better:I’m not talking about the check. I’m just trying to point the inconsistency between two cases about the same thing.
But that's the thing, they aren't the same thing. One is trying to access something not declared in the scope, while the other is trying to access a property of something that is declared in the scope. You can test both with
typeof === "undefined", but if you try to access something undeclared you get an error, which sucks, but that's how it works. Nowadays is very difficult to end up in this scenario tho, because editors will let you know if you're trying to access an undeclared value.Json can model undefined through absence of a key. Otherwise fully agree.
When reading a model, I would generally consider absent or undefined values an error, or eg a just in time migration that needs to still happen :)
Otherwise the contract is too fuzzy for my taste
That’s the thing.
Eg from a json response, a “post” which has “deleted_at” is null means it’s not deleted. But if the “deleted_at” is missing, it should be considered as invalid api response
That depends on the API implementation. This is like saying:
Which is kinda like a Straw Man. If the API has a poor implementation that requires
null, that doesn't mean you need to usenullin your entire codebase, that just means you need to use it in your surface of contact with said API.Sorry but I wouldn’t consider an api which has consistent response for nullable object, as a poor implemented api..
Me neither, that doesn't have anything to do with
null. You can just omit something when is "nullish" which translates to .... you guest it,undefined, and it has the added value that payloads are smaller 🎉Absence and null are different things, as said.
What constraints are you under that these minor payload sizes matter? you’ll probably get more out of gzip than omitting nullish.
Payloads are smaller by omitting some characters, good.
But if it causes confusion between null (value is set as no value) and undefined (value is not set), I wouldn’t take it as a good thing.
"Absence and null are different things" ... how? The size of the payload doesn't only affect the request, also the parsing. You're literally loading something trough the wire and in memory that you could omit, just to use
nullX_XWith tech like TS, contracts are actually pretty easy to define. There are tools out there that turn all kind of type definitions in
.d.tsfiles, that you can use from TS or JS with editors such as VSCode to get the type checking. If an API defines something as optional, in TS that'sType | undefined, so you can omit that key or send thatTypeand it will work :D ... if the types change in any way, you should update your type definitions accordingly (using the same tools), and you'll get errors directly in your editor 🎉undefined😉undefined.nullorundefined, so I still don't see much value fornullin this scenario. If a value can either come or not, then you can define it as optional (meaningType | undefined), and in the front end you can just dovalue ?? "default value"orif (value === undefined) { throw // ... }or whatever 😄undefined🎉“Why would you ever need to make that distinction? Both are "nullish", and you don't need to differentiate between "intentionally missing" and "unintentionally missing".”
This is quite often needed? How do I know if a user set this as “undefined” or if it was not set by the user. Hence the need for null.
When a value is "optional", that only means it can be defined or not, it can be a type or be nullish (the
Maybetype I mentioned). So something that's optional has the shapeType | undefined, orType?So if a value for a user is let's say a string name that might be intentionally left blank or not, then the type for it is
string | undefinedorstring?. So if the user set it to be empty intentionally, then you can set the value to"", and if not then set it toundefined.You can then send it like this if the user left it blank intentionally to the back-end:
And if the user didn't set it at all, then:
Trust me, every piece of code written using
nullin JavaScript/TypeScript, can be written withoutnull, and it generally ends up being cleaner.Sorry but when a value is optional, it doesn’t mean it can be null.
Eg a “pizza” with optional “extra_topping”. If you don’t want to have extra topping, it should be null.
But if it doesn’t have “extra_topping” (value is undefined or absented), the object should be considered invalid.
Why?! If it doesn't have
extra_toppingthen it doesn't have any, and if it has that property then it has extra toppings ... the one defining that "rule" that if something is missing then is invalid is you.If you call a pizzeria to order a pizza, you don’t mention extra toppings. What should they think?
1) You don’t want
2) You forgot to mention
So much mentions of pizza are making me hungry 🤣 ... Answering:
Do you say "keep the default toppings" every time you order?
You didn't answer the question. The question is how would the pizzeria know if:
1) You don't want extra toppings
2) You want but you forgot to ask, or you didn't know that they offer extra toppings
1 should be null, because you stated it explicitly
2 should be undefined, because you haven't decided yet (yes, AKA you haven't defined it yet, that's why it's called undefined)
Did you ever actually bough a pizza? 🤣
Maybe this is not the best analogy for your point about needing 2 different nullish values?
Alright, if you consider it's problem as a client, I have nothing else to say :)
// And you're right, I've never bough a pizza actually..
Gotta love how you completely skipped over the issue.
So how in the above do you suppose isSet would work since all you’re talking about is typescript types which don’t actually exist at runtime.
const settings = something.
if(isSet(something.option)){}
Using falsy values is like pointing a gun directly to your foot ...
And you'll get:
Option: Intentionally unsetif the value is"".Option: fooif the value is"foo".Option: Not set yetif the value isundefined.Really, you don't need
null, or at least not for this.Yes... because everything is a string and everything is a single type. 🙃
All this post comes down to is “I don’t like null”. You don’t seem to have an understanding of the use so you make up reasons for it not needing to exist.
Don’t like it then don’t use it but it exists for a reason and the fact you can’t accept that shows what kind of developer you are.
It seems somebody skipped over the disclaimer at the bottom of the post! 🤣 ... should I assume that you can't live without two different nullish values, then? How do you think languages that only have one of them work? How do you think languages that don't have nullish at all work? If other languages can work without two different type of nullish values, why JavaScript can't?
Obviously there are different types, and different possibilities for optional values, that doesn't mean we then need to do all types
Type | nulland rely on falsy, that only means that if you have different types, you need to deal with them in different ways. Usingnullalways is a really poor solution.And if the number of downloads of the ESLint rule to avoid null tells us something, is that is not "only me".
I've been using Lua for years and never felt the need to have a second non-value. You either have
nil, or you have a value.If you want a special value that's distinct from everything else, you just use an empty object and compare to it explicitly.
That's an argument that doesn't hold up under further scrutiny, though. The "single nullish type" causes its own problems that JavaScript actually resolves.
Let's take Java as an example. It only has the single type
nullwhich is the default variable assignment for any object type. The "Map" interface in the Java standard library (Java's dictionary data structure) specifies that it returnsnullwhen client code gets a value from the map using a key that the map doesn't know about. Unfortunately, it's well-known thatnullis often also an actual value associated to keys in the map. This single flaw is one of the leading reasons why NullPointerException is the number one exception observed in the logs of production Java applications.That situation is resolved in JavaScript, because the absence of the key is represented as
undefinedrather thannullwhich disambiguates the cases.You mean to say that all languages with only one nullish value, or no nullish value at all didn't figured out something that JavaScript did? In any other languages, if something is "optional" (meaning it can be a certain type or nullish), you just have to check before using it. If you don't, you run into issues (one of the main reasons some languages have the concept of
Maybeinstead of having nullish). In JavaScript you can still run into exceptions (TypeError) if you try to run something that's nullish or try to access a property inside a nullish. Both are solved using nullish coalescing and optional chaining, so you can still use either one of them. The solution to the problem isn't having two different nullish X_XWhat I'm pointing out is that it's not a very good argument to say, "if other languages can work with a single null type, why can't JavaScript?" Just because other languages figure out ways to exist with only a single nullish type, doesn't mean anything in terms of the advantages of having more than one nullish type.
As a complete aside: you're actually making the argument for more than one nullish type yourself when you deflect to creating an optional or maybe type. There's also the "Null Object Pattern." These are all attempts to grapple with legitimate issues in programming. Using "null" and "undefined" together is just a different attempt.
At the end of the day, I could avoid the use of both null and undefined in my programs. That doesn't make it a good idea. I saw that someone made an HTTP server in pure BASH. Interesting? Sure. Does that mean I don't "need" HTTP servers? No.
But the argument about languages having only one nullish, or not having any, is to point out that generally "nullish" should be avoided, and with dealing with it we should be as straightforward as possible. Having 2 nullish is the contrary to avoiding nullish 😅
The article shows several scenarios in which you might need nullish, and how you can deal with it only using a single nullish value (
undefined). Those same problems could be solved only usingnulland avoidingundefined, but you end up doing extra work when you could just use the nullish that the language uses.Why not? Is commonly known that nullish values are a bad idea (the creator of the null pointer itself regrets that decision). My point in the article is that if you have to use nullish, at least you can use the one that JS uses.
Is there an example in this article that's using
undefinedand makes you think: You neednullfor that?The whole "other languages do well without two non-values" argument doesn't mean that JavaScript should do the same; it just means that there's no need for JavaScript to specifically choose this solution to the problem.
If you want an actual reason not to have two non-values, it's that this just seems very arbitrary. Why two, why not three? And if you're ascribing more and more semantics to the different kinds of value-absence, shouldn't you also expose this possibility to the user? At this point, just let users define their own values that are treated as falsey by the language and add a
FalseySymbolcounterpart toSymbol.I don't think it seems arbitrary at all. The language specification is not arbitrary when it describes
nullas meaning something explicitly set andundefinedmeaning something, well, undefined. Just because they both evaluate to "falsey" doesn't mean there's not a clear difference to their actual meaning. The reason those two exist (and not three or four or five) is because there's not another clear cut solidly-defined use case for it. But for bothnullandundefined, they both have a clear unambiguous meaning and plenty of use cases.You misunderstood my point; both
nullandundefinedare not at all arbitrarily defined; what is arbitrary is exactly having two of them.If you're going to distinguish non-values of different kinds, why only two? Why not add a third one for "no selection", or a NaN-like value for "unknown" values? And I'm sure many problem domains would come up with their own non-values that make absolute sense to distinguish from
nullandundefined.As I said, at that point a more elegant solution would be adding
FalseySymbolorNullishSymbolor both and leave it up for the user to define specific non-values that hold whatever semantic meaning they want.This is a neat case where there's exactly two reasons why a value could be empty. But in other cases there could be many different scenarios.
If you really want to use your own "empty" value, you can simply construct a symbol to represent that specific state
Hi, I would strongly disagree with this statement. In my opinion you definitely want to use explicit null in many cases. The thing is, null is a quite handy tool if you use it properly (eg. "Hey, I want this value to be unset but defined!"). Also, I noticed you are considering
typeof null === "object"as a bug, which really annoys me. It is not a bug and the reason is lying in the fundamentals of the language (prototypal inheritance). Please be careful with such a statement, since it is misleading and generally wrong! My apologies being short and not going into the details though. Happy new year!While this is by design and therefore not a bug, I think this combination of behaviours is what makes for a very bad design. If
nullis an object, then it should behave like one.But .... it is a bug, it was never patched because ... welp, that would break the web (like many other things).
My point is mainly: Why do you need to do that distinction, when they are both "nullish" values? How do you think languages that only have one or have no nullish values deal with this?
For me at least, is kinda like the chicken and egg, folks don't actually "need" null, they generally just find ways of using it. Just for your consideration, there is a disclaimer at the bottom of the article in which I clarify that "you don't need" doesn't mean "you should never use", is just like saying "you don't need fast food", meaning you can survive without using it and you should avoid defaulting to it to solve issues that you can solve with better tools.
Sure, I'm considering it not-a-bug for the same reason, it is just the part of the language and you have to deal with it :) However, you can exploit its potential. I personally prefer using
nulloverundefinedwhen I want to consider an asset to be explicitly empty (but still defined in the scope of the current code).undefinedis the default value for a non-existent value and I think it is unsafe to rely on this, because it can cause runtime errors more often (think of globalwindowin Node environment). It's all about practices and personal preference. In my opinion,nullis a handy stuff and I'm happy to use it.100% agree with this! My series of "you don't need" is not about imposing my style, is more about making the readers wonder: Do I really need that? Or I'm just used to it? I used to be a C++ hardcore fan, class all the way, and my first entry in this series is saying that we don't need them because I feel happier not using them in JS/TS.
About
undefinedintroducing bugs, the vast majority of them are solved with the good??and?., so you can actually do stuff like:Yup, exactly. Hail to nullish coalescing and chaining! I love how people eager to build the language forward and constantly improving it in every year. Classes are not quite making sense in JS to me too. Although, it is important to be able to adjust your personal preference to the environment your working in. Everyone is doing stuff differently, but we usually work in teams, so... you know the drill.
Indeed! My No.1 priority at work daily is making my teammates lives easier, building components, utils and libraries for them. More than once I had to change something I personally like to something that adjust to them better, and that's ok. They are all happy without having to deal with classes, but if they weren't, I would be doing classes even if I don't use them in my personal projects, just to adjust to them ❤️
No ... you're saying "folks that use it a lot ... get pretty mad" - I'm not mad at you lol ... but I still see value in explicitly assigning null, and undefined is a distinct case.
The point is that "undefined" is automatically there, while what I want is for programmers to explicitly and deliberately initialize their variables - be it to null, or zero, or an empty string, or whatever makes sense. But explicitly initializing a variable to "undefined" makes no sense (because by definition it already has that value).
If you really want to get rid of null pointers and of "having to check for null everywhere" then you should introduce an "optional" concept (like Rust has, or even Typescript).
We have "optional" with JS, you just use default values (only works if the passed value is
undefined), nullish coalescing and optional chaining (this last two work withnullas well). TS after all is just a superset of JS, the types it uses are just the explicit version of JS types (also seen in JSDocs).Thanks for not getting mad. Trust me, there are some folks here pretty vocal about their opinions not matching with mine 😅
People should remain rational, getting all emotional over tech debates is a bit stupid lol
No matter how much you don’t want to use null you’re going to end up using it anyway. It’s going to come in from libraries, input, and data sources.
You still don't "need" it, you'll just have to use it when you use those libraries. You can also use validation libs to turn nulls into undefineds. The important thing is avoid using it yourself in your code, not su much the contact surface with code outside your control.
You don't "need" stoplights but not knowing about and incorporating them into your driving will have disastrous results.
That's not a good analogy -_- ... I never said you don't have to know about nulls, how they work and how to deal with them if you find them, I said you don't need them in your code, and you are generally fine with
undefined.I mean, you also haven't said to "deal with them if you find them" (which by the way means having null in your code).
Might be a language barrier that's complicating this conversation, but I said this already several times in the comments: You should keep
nullin the surface of contact with things that "need" null, like APIs. That means that you'll have to writenullto interact with those pieces of code that didn't figured out how to work withundefined, yes ... but still doesn't mean you need to writenullin the code that doesn't interact with those APIs.You can keep
nullin the block in the middle there, only. And in your code just useundefined. Think it this way, if you invite a celiac friend to eat outside, you need to buy something gluten free for them, but do you eat gluten free as well when you don't need to?It sounds nice, but null is part of the JavaScript language itself. It has an explicit meaning in the specification:
So that "surface area" you're talking about isn't going to be limited to APIs. And when you share a JS codebase with anyone else, the approach of not using
nullis going to be untenable, and, even if you don't share, your JS is not going to be idiomatic.I mean, I worked for years now with different teams without using
null, and nobody misses it or says "we could solve this withnull". I said it on the article already, but making that distinction between "intentionally absent" and "unintentionally absent" doesn't make sense, because is just "absent" and you need to deal with it the exact same way.That doesn't mean it's idiomatic.
I can see that it doesn't make sense for you, but look at all the commenters here with plenty of examples where it makes sense. And I even gave you one in another thread.
If we have to use this article as a metric, we have 10+ negative comments and 10+ folks liking those comments, so let's say 20+ total. And then we have 55 likes to the article itself, 17 unicorns, and 6 positive comments, so let's say total 70+ ... I knew I would have some folks that wouldn't agree (that happens with every article in this series). You keep saying is not idiomatic, and my point is that you're just saying "the same thing with two different words". The distinction is not needed, you might use
nullto make that distinction between two "optional" values, but you don't neednullfor that.The point of the article is to actually make you think if you need it, I know some of you are used to using
nullor you do that unnecessary distinction betweennullandundefined, but the idea is to make you actually think if you need that distinction, or not. Have you actually tried to code without usingnullyourself? Maybe you already did, and you missednullevery single day, but my experience (and the experience of many other folks) was the exact opposite.The point about idiomatic JavaScript has to do with working with JavaScript developers. If you write in a non-standard (not idiomatic) way, you make it harder for others to use your code or integrate with it, and that can lead to bugs.
These reasons to use null seem compelling to me:
How can use less ""features"" makes something lead to bugs? The only way of leading to bugs is if a dev assumes that something will have a
nullvalue, for example, and my function returnsundefinedinstead ... but the same could happen the other way around :/ (not to mention both are solved the exact same way)Being in the language is not a "compelling" reason, because we avoid things in the language all the time that we know are bad.
evalandwithare still part of core JavaScript, and you wouldn't use those because you know that they lead to issues of all kinds.I already said that you can then deal with
nullonly when working with those. Not to mention that data sources can also returnundefined, it depends on how are they made, and the actual user input is nevernull, you can get "null" of user input only if the handler for said input returnsnull.So for you idiomatic is: It's in the language + folks use it. From that definition, being "idiomatic" is not a good reason to use something from my point of view.
I keep asking why is that distinction even useful? They are both missing values, both needs to be handled the same way ... so why is useful to differentiate one from the other?
Exactly. Consider the matches() method on string. It returns
nullto indicate that there are no matches found. Let's say we take your approach and wrap this in our own method where we returnundefinedfor this case instead. Now you have a client of your method who is a JavaScript developer. They check fornullto see if no matches were returned, but you have overwritten this withundefined. And that's a fairly benign case.This is often known as the Principle of Least Astonishment or Surprise. Users of your code should have an expected experience.
Do you actually do
if (matches === null)nowadays? You don't need a wrapper. You can use nullish coalescing and avoid a lot of pain with bothnullandundefined(again, you handle both cases the same way). So you can do something like this:Which works with both
nullandundefined. Instead of having to write:And I still don't see where the distinction between
nullandundefinedis valuable here. With the same example of the util, if someone does a wrapper of something that generally returnsundefined, and they make it returnnull, it could cause confusion as well. You should't code assuming the nullish output of a function, and you still can be resolved similarly to the above issue.BTW, if I had to write a wrapper for
match, I would do it something like this:This way you can not only curry it, but always be sure you'll receive an
ArrayLikefrom it instead ofRegExpMatchArray | nullwhich is far from ideal.It seems that you asking this question proves the point. You have no idea what I (or other clients of your code) are going to do. I may do that or use the fact of
matchesreturningnull(and notundefined) in probably a few dozen different ways. And when/if you change that, you introduce the potential for bugs, as previously pointed out.The idea of a "wrapper" came from your comments, not mine, though.
I am not sure but I'll give the benefit of the doubt here and assume that you are not willfully missing the point. So to reiterate again: yes that could cause confusion as well, and the very fact of this confusion is why JavaScript developers should not ask "Do I really need to use
null?" and should, instead, use bothnullandundefinedconsistent with general practice. It is the standard practice (the idiom) in JavaScript fornullto represent a purposefully-set value andundefinedto represent a value that was never set, and this is in the language specification as well as the canonical books and documentation.My point was that you shouldn't assume the output of a function. In your previous comment you basically said that you "expect" something to return
null. Based on your same logic in this new comment: "You have no idea what I (or other clients of your code) are going to do", so why is bad for me to assume you'll know that you shouldn't domatches === null, but is good for you to assume that the function will returnnullinstead of actually checking the return type of the thing you're using?But that's the thing, if you use a function assuming it will return either
nullorundefined, why is it on me as the dev and not on you as the consumer of that function? I already said this, but the other way around your same argument applies, if you create a function that I expect will returnundefined, and instead you decide that it will returnnull, then it's my problem that I was expecting your function to return something I wanted it to return, instead of checking the typing or using something like??.And I still don't see where the distinction between null and undefined is valuable here. With the same example of the util, if someone does a wrapper of something that generally returns undefined, and they make it return null, it could cause confusion as well.
Trust me, I'm giving you the same benefit. I added sources to the article for y'all to explore, because my personal experience of not using
nullin personal projects and at work, and not missing it at all, and explaining that the "distinction" between intentionally missing and unintentionally missing value is pointless because they are both missing and need to be dealt with the same way. Maybe checking some other sources besides my article adds some light to the subject, but ifnullis so necessary, how is it that Angular is considering droppingnull, TypeScript doesn't usenullin its source, and folks like Douglas Crockford, with years of experience in JS, don't usenullat all and just useundefinedinstead?As @darkwiiplayer pointed out several times already, that "need" for multiple nullish values is trivial, having 1, 2 or 10 different nullish values with differences you define with conventions, doesn't take back the fact that they are all still just nullish.
Every time I ask: Why do you need to do that distinction?
The answer is pretty much "because
nullexist in JavaScript for that", but that doesn't actually answer the question. There are several things that exist in JS to be used for something, and we realized that there are better ways to do that same thing and not use that feature (evalandwithare the two that I mention constantly).We might never agree on this, but the questions I leave to you are:
Do you actually tried to code without
nullat any point? Do you ever worked with a library/framework or codebase withoutnulland missed having it? Because I sure did tried doing it "your way" in the past, and once I did the "transition" I never went back.A valid use case for null is when you want to iterate over object properties and do some logic if corresponding values are empty. You usually don't want to write things like typeof string && "". What about numbers? What about booleans? Angular for instance uses it in its Forms Module. If a non-string field is empty, its value is null. If you reset any field value its default is null. So to me null is absolutely legit and not synonymous to undefined.
That doesn't make much sense for me. If something can be either
string | undefined, then adding| nullto the mix doesn't help much here. You end up having to test for yet another type, when you could just keep it simple.This two behave exactly the same:
So
validatethere could be implemented like this:Which would work exactly the same for both
nullandundefined. What's the advantage ofnulloverundefinedhere?And about the argument of "Angular uses this in its Forms Module", just because a module/library/package uses
null, that doesn't mean that you need to usenullall over your app as well. Not to mention that if a non-string field is empty, it could beundefinedas well, same if you reset it, is just an implementation detail of that forms module, which doesn't mean you need to usenullin places where you're not interacting with that particular module.Good points! My examples were not that good. Still, in some other place of my program I might want to directly access the object property like foo.bar and if it is undefined how can I be sure that the property even exists? I mean they are semantically different and have slightly different behaivior and I embrace them both as features of the language. You are correct in your wordplay, you don't need null, but I want to extend this and say that you may want it. For consistency, readabitity, because of business requirement or for whatever other reason. This is not a "goto"-situation. If you choose wisely your code will only get better. Like in Typescript there are types any and unknown which are in many cases interchangeable without any side effects, but... unknown is safer and sometimes you really need any.
You might be one of the first devs to comment here with the "opposite view" but you actually understood the post 🤣 ... Indeed, the idea is that you don't need
nulland you can useundefinedto express the same things better, but if there's a library that only works withnull, then you have to use it there. The main idea is that we shouldn't default tonulland just use it when is actually necessary. Some folks think is necessary to make a distinction betweennullandundefinedand I (and many other devs) disagree with this because they both represent a "no-value" or "nullish", but that doesn't mean "you should never usenull", that only means "you should use it when is actually necessary", like APIs or libs that only work withnull.About
anyvsunknown, I might write a "you don't need any" showing how you can useunknowneverywhere, safely, even if you want to be lazy as you'll be using justany☺️I might be wrong, of course, but I am still not as convinced as you are. They are simply different and we would probably have quite heated debates regarding some special cases if we were working in same team. And I would also install a swear jar for such words like "nullish" or "falsy". My code is strict and explicit af 😄
Although, to be completely honest, I also have to admit that we really did forbid null in our previous project and mapped all null values as undefined in all our API responses. Didn't regret this decision, in our case (with good models) undefined worked as "empty value" really good, felt natural and the only places where null was really inavoidable were API and Angular forms.
Btw, I have a good example where you really need any. It's JSON:API standard which defines one of the fields as object, so the best you can do on the most abstract level (before any extensions with type narrowing) is something like Record<string, any>.
But you could also do
Record<string, unknown>, which is like saying: I know this is an object, I just don't know the type of the properties inside of it ... way better than havinganyand accidentally calling a method or passing it to a function without first checking what that is. I seeunknownas strictany, you can't use it unless you first figure out what it is. And I love it.I also code pretty strictly and I still see not benefit using
nullat all (unless, as we said already, I'm interacting with something that needsnull). And I share that experience you had, of migrating codebases to only usingundefined, or having thatno-nullESLint rule, and the best thing about that experience is that not only me, but nobody in those teams missednull(that's also why I shared that talk given by Douglas Crockford, because he had the same experience, years ago).You're right. And I am stupid. Of course, unknown can be narrowed same as any. Simply forgot that.
You're not stupid! I used to use
anyquite a lot beforeunknownwas introduced, so it's ok if you're still checking before using it. The main difference is thatunknownforces you to do that check, but if you still do it is ok!The thing is that today I really never use any (except for Angular and such). Absolutely forbidden! But I also wrote the implementation for jsonapi before I learnd unknown. So... 🤷😄
That's what happen to folks like us that were in this TypeScript game for a long time x'D
Definitely food for thought!
I personally over rely on null as it works so nicely with databases, but that is just laziness on my part and you are right that stripping null values instead of validating them (which does require some thought so you don’t open a massive security hole) does seem cleaner!
Next side project going to give this a go!
Now can you please stop this series as I am running out of things I can use! 😜🤣
Let me know if you hit any wall while trying to get rid of those nulls, and I'll do my best to help out. In my personal experience I even noticed some speed bumps in requests/responses because payloads were always smaller 🎉
Thanks Luke, great to know as I am sure there will be something and I imagine there won’t be much out there in terms of advice on something like this! 👍
I've seen people give entire lectures about when you should use undefined and when you should use null. I always found that a bit silly. We have better things to waste our attention on. I just always use undefined unless forced to do otherwise (by some library that does
=== nullor such).I try to use undefined wherever possible. However, null can be useful for asynchronous operations. For example, if the user object is undefined, the initialization phase has not been completed yet. Otherwise it's ready to use. If null, there is no user. Login is required.
Patch api; omit (which is usually synonymous to undefined) is “not changed”, null is empty value.
You took string as an example, but think also about numbers (-1?) or objects etc
We could change our API handler functions/methods in the front-end so when they do a patch operation we turn the intentional unset values into
nulls that the back-end will understand. Still, an API that usesnullfrom the front-end to clear values in the back-end doesn't look like a great idea in general. Ideally you should have some kind of option in the endpoints to unset values, if we have updates and unsets in the same patch endpoints it feels like an accident waiting to happen.What’s the difference between an unset and an update?
With update you're just changing one value with other, while unset is closer to the
DELETEoperation. In graph based DBs like DGraph, or No-SQL DBs like Mongo, is quite dangerous to just unset a property of something. Let's say accidentaly you set this with a PATCH:And
friendswas an array containing all the friends of said user, suddenly we deleted all friends in a patch operation. Ideally actions like that should be in a different endpoint, not the same one you use for updates (not to mention this wouldn't even be possible if we don't usenullat all).What about
totalCost: {
amount: 50,
currency: “EUR”
}
Just creating a sub resource for totalCost to be able to delete it, is not really optimal.
Not to mention you may use it in an atomic batch of changes
But in that scenario, for example ... why would you set
totalCosttonull? The default should be{ amount: 0, currency: "EUR" }or something like that. If you want to leave the default, you omit it, if you want to update it to the default then you just set it to{}(maybe even the wrapper function you have to deal with API interactions can do that for you), and when you want to set a value then you just set it{ amount: 50 }or just the currency{ currencty: "USD" }or both{ amount: 50, currency: "USD" }.My point when talking about this in the article was that when you need
null, you actually need aType | null, which can be easily replaced withType | undefinedor even justTypebecause maybe you didn't even needed that nullish in the first place.Don't take my word for this, you can just google about languages with a single nullish value or without them, and see how they solve this issues ... that can be easily transferred to JS, using only
undefined😄We certainly could do that, but it would violate the business needs.
We can use custom null objects to mean no-value, but why? Just to reach a metric of not using null?
Another way is to represent Options or Maybes or Eithers as tagged unions, but if you have null at your disposal, and mind you, it is part of the json spec, so even languages without null or with just one nullish value, can still leverage absence (aka undefined), and null when speaking json.
It certainly makes interacting with your api easier and more standard :)
How you interpret these values while deserialising json, or when you play the object dance in domain model, or how to store and represent in your persistence model; those are private implementation details.
We might not be understanding each other, if the API needs
nullto work, then use it when interacting with it. The article doesn't say "you should never ever usenull", the point of the article is that we shouldn't default to it and useundefinedinstead. Instead of havingnullandundefinedall over the code, you should use justundefinedin your code, and if the API is limited to usingnull, then just usenullin the functions or methods that interact with it.I have a disclaimer at the bottom of every article in this series because folks generally understand "you don't need" as "you should never use", and that's not the point. This series is about re-evaluating our defaults.
There would be more of a case for
null/undefinedas separate values if something like this:actually worked, that is, returned
{key: "value"}; but instead, JS still treatsundefinedin the second object as different from not setting a value at all, which makes this whole distinction very pointless.Api to update profile details have several optional fields. One of them is display name.
Null = no special display name, use account name. If there was a display name, replace it with null.
Omitted = don't change display name.
String = new display name to use.
Now you just entered the realm of "let's use ugly tricks to represent null like 'empty string'" or add another field that says 'delete name'. Used empty string? Congratulations you can no longer use standard name validations (that usually occur before your api handler) because you'll get name too short or empty before even doing your ugly hack that treat empty as null.
Null have a contract meaning. Now you'll answer me like you answered everyone else "just use undefined bro" because honestly reading your comments it sounds like you dont even listen to what people try to teach you.
Go ahead, write poorly designed APIs. I'll use null, thank you.
PS and that "let's save few bytes" argument is something a junior dev would say. It's meaningless. If you really care about it use compression. Or better, use binary format and not json. But if you chose json go with proper json and don't uglify it to save few bytes. If your api use json it means you are OK with the extra overhead otherwise you wouldn't use json, and being cheap in few bytes is absurd.
That's like copy-paste the same line 10 times instead of using for, to save the for() overhead....
So you find
""and the typestring?uglier thannulland the typestring | null | undefined? I guess we agree to disagree. And that second part about validations, what is that you can't validate about a string if is empty? I mean if you usenullthe validation might look like this:And look at this huge change if you used an empty string instead:
I mean, that being the case, you wrote a comment like everyone else pointing to this single problem with patch like APIs, when the article is about avoiding
nullas much as possible:nulleverywhere, whenundefinedis perfectly fine.IDK about you, but everywhere !== somewhere. And my answer is always the same because you're pointing the same thing. My answer would change if the comments where different. I explained it in the article itself, but is weird that you need to type everything as
Type | null | undefined, like you need 3 different states for everything, when other languages are perfectly fine with 2 (Type | nullish).OK, go ahead and keep using it, I guess your answer to my question in the article is that because is useful in this patch like scenario, then you have to use
nulleverywhere. I already explained that you could keepnullin the contact surface with the API only, and avoid it everywhere else, but I guess that's not enoughnullfor you 😅Of course you had to go and say "You're a junior if you don't do things like I do them". JSON parsing is really fast, but skipping nullish values is faster not only because of the actual transfer from the back-end, but also because of the parsing itself. So you're making your responses smaller, your requests smaller, your parsing faster and your type checking easier, not to mention testing which doesn't need to check for 3 different types. But let's better use
nullbecause is less "ugly" and junior-like, right? 🤔Talking about
for, you might not like my other article talking about it 😅EDIT: lol you got utterly btfo so you hid my comment and blocked me from writing new comments? Coward. Hope you at least learned something.
EDIT 2: you're still a coward and continue to reply to me while I'm blocked. but you don't even worth the comment I wrote. bye.
Second reply below, before I saw you blocked and edit this:
Im talking about null in js and you answer in typescript. If you want to talk about typescript say "you should not use null when using typescript". Your article seems to be talking about js and ts alike.
Are you really that much of a junior that you actually write your api json validations manually with if-else, and not using a proper schema validators? Have you never written any API before or are you just being dishonest when presenting your "solution"?
You wrote your article in a very strong, clickbaity tone, then you go and hide behind that tiny disclaimer in the end. Stop acting innocent. You wanted to write an edgy and controversial opinion? Own it! And if you can't or don't have the balls to do so, don't write so boldly. Just say "why you should sometimes prefer undefined over null". But that's not cool and edgy and click bait enough isn't it? So you want to eat the cake and have it too, by putting your little disclaimer in the end. Real mature.
Which brings me to the next point - why I insist on putting you back in place - I know juniors like you, you try to make a name of yourself by spewing out controversial edgy nonsense that may sound like they make sense, but are actually really bad. Problem is, other juniors pick up this nonsense and repeat it to sound cool and end up writing bad code because of that. Much smarter and experienced people than you decided null was necessary. There's a reason for that. You don't have to try and butcher every holy cow just for the sake of it.
Json is not extremely fast, it's extremely slow compared to pushing bytes array, but its fast enough for most APIs which makes it ok. When you choose JSON for your API you make the decision of readability over performance, and that's OK for 99% of cases because json fast enough. But if you made that decision, you don't turn back and later uglify your entire API just to do tiny optimization. That's something a junior would do. Either your API is performance critical in which case you dont use json at all, or its not in which case you try to keep it clean. I mean, you don't rename keys from 'userName' to 'un' just to save few bytes right? That's equally dumb than omitting fields that should be null, and probably save a lot more bytes. But I do hope you don't actually do that...
In summary- nulls are useful, stop trying to sound edgy then run back to your disclaimer, and you dont sacrifice readability for performance when using json.
I know you're new here, but the community of DEV is based on respect for each other, even if you don't agree with something, you keep it respectful. You already started saying this was "ugly" and the solution was "junior", but I let that pass because you're new and maybe it was a language barrier ... but then you went and insisted with the junior thing, "lack of balls", being immature, which was more ad hominem than an actual argument. To top it all out, you added that "coward" thing when you noticed you got blocked.
If you don't want to keep getting blocked by other users or banned from DEV altogether, I suggest you lower that a notch.
Cheers!
Oh I used to care about this stuff, I don't think I do anymore but your post captures the arguments exactly. I don't care so much because I know how wild JavaScript is about falsely and truthy values compared to other languages. Best not to loose sleep over it 😅
You should never loose sleep over anything code related, even more so if that code is JS 🤣 ... the articles in this series are more like "thought experiments" to make the readers wonder if they really "need" given thing to code, of if maybe it makes sense to not use that. I share the things that make me generally happier when coding, but that doesn't mean it will make everyone happier (as proven by some quite vocal users, raging on the comments 😅).
Good explanation.
These toxic pointless posts with title like "you should never ever use.." should banned from self-respecting tech publications.
Javascript is grownup language, and everything regarding it is already written in nice books and articles. Stop inventing a wheel, discover America, recreate alphabet with posts like this. Either add something valuable, innovative or stfo.
It is my first comment in Dev. As a JS veteran I regret spending time on this post.
Happy New Year!
Best wishes to you op, hope you won't be offended.
Good article.
Cheers!
Somehow it would figure someone making ill educated and nonsensical claims about something we "don't need" would be suggesting linters. Given the endless wreck of nonsense they seem to like by default like kvetching about Yodish, drop-through on case, and so forth. The stuff linters seem to do is all silly pet peeves of people who don't seem to actually know how to program, or screaming "I'm too dumb to understand this, so nobody else should be allowed to use it.".
Reinforced by the titles of your other "You don't need" articles, and how each of them jumps through some crazy hoops of fire for the bloated alternatives.
Sad. Very, very sad. You can debate and argument about that, but the only reality is that null and undefined are completely different, and should be used differently. No more no less. The rest is trolling.
Ohh yes, the good ol' "I'm right, the rest is wrong" argument without actually adding anything to the conversation. Maybe when you said "the rest is trolling" you were making a reference to your own comment? 😅
I spend a little bit of time to read all comments, and as I see you spend a lot of time to explain the unexplainable. Other people have responded about the differences between null and undefined, and each time you come with tadam, but no ... Null is defined without value, undefined is not defined. So, what else ?! .. :)
So you read the post, read the comments and then added your own just to say "sad very sad" instead of explaining why from your point of view that distinction is useful at all, and how other languages that don't have two nullish values survive? .... Ok, thanks I guess, commenting boost the visibility of a post, but maybe next time try to engage in the conversation so others can learn from you and you can learn from others...
An arrogant developer is the worst kind... Dude you are not right, accept it. So you think all who contradicts your opinion are stupid? :D Hope you do coding as a hobby!
So thats why you are censoring comments? The truth hurts?
-- edit --
I'm reading this site for years and most of the juniors are humble enough, and here you come, kicking in the door with this amount of arrogancy and to possibly teach others bad practises, you are disrespecting us with this.
You are also confirming all these with the blocks... You know @anonyme was 100000% right and all you did was getting offended instead of learning from more experienced people.
I guess you are the one who tries to justify bad stuff when you get a PR comment.
There's a code of conduct against disrespectful comments, and you created an account just to comment something that doesn't add anything to the conversation just is plain old Ad hominem. Forgot to block you, sorry about that. See you!