DEV Community

Cover image for You don't need null
Luke Shiru for Vangware

Posted on • Updated on • Originally published at lukeshiru.dev

You don't need null

While a lot of programming languages that have a "nullish" type (null, nil, etc.) debate about avoiding it, JavaScript is the only popular one that has two, you read that right, two nullish types. One of the most common recommendations is to stick to using only one, and my recommendation is to only use undefined and avoid null. In this article, we will go over the reasons you might also want to avoid null in JavaScript and TypeScript.

Why is so common to avoid nullish values?

The creator of null pointers (Tony Hoare) is known for calling his own creation a "billion-dollar mistake":

I call it my billion-dollar mistake (...) My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

When we use nullish values, what we want to express is that something is "not there", a "no-value". Generally, in typed languages, we express those as "optional values", because they can either be set or be nullish.

The direct implication of this is that we need to test every "optional value" for its type and also for the nullish value it can take.

Now, imagine how bad is it for a language to have two nullish values. We now need to test for not 2 different types, but 3. This affects negatively maintenance, readability, and overall code quality. Because of this is that the most common recommendation is to avoid nullish as much as possible, and in JavaScript, try to stick to using only one. In the following sections, we will go over some reasons why I (and many other developers) prefer undefined over null.

The nullish that the language uses

As Douglas Crockford (the father of JSON) put it in one of his talks, JavaScript itself uses undefined all the time, so let's use the one the language uses:

let something; // This is undefined!

const otherThing = {
    foo: "hi",
};
otherThing.bar; // This is also undefined

const aFunction = anArgument => {
    // anArgument here is undefined if no value is passed
};
Enter fullscreen mode Exit fullscreen mode

To use null on all those scenarios, we need to explicitly set the values to null, which will look like this:

let something = null;

const otherThing = {
    foo: "hi",
    bar: null,
};

const aFunction = (anArgument = null) => {};
Enter fullscreen mode Exit fullscreen mode

I don't know about you, but for me...

That's undefined with extra steps

What if I want to define a nullish value intentionally?

In that case, just assign undefined to it:

const anObject = {
    ...otherObject,
    propertyToNullify: undefined,
};
Enter fullscreen mode Exit fullscreen mode

That nasty bug with the type of null

We all know at this point about the bug with typeof null, that bug doesn't apply to undefined which works as expected:

typeof null; // "object" 🤷🏻
typeof undefined; // "undefined" 🎉
Enter fullscreen mode Exit fullscreen mode

Why would we use a bugged value intentionally?

Smaller API responses

API response sizes are reduced drastically if we rely on undefined instead of null. Here's a response example using null:

{
    "foo": "foo",
    "bar": null
}
Enter fullscreen mode Exit fullscreen mode

Versus with undefined:

{
    "foo": "foo"
}
Enter fullscreen mode Exit fullscreen mode

The case with Array

Array is a special case, because when we create a new array of a given size, the items inside said array are actually empty, not undefined. This empty means that if you check for their value, it will give you undefined, but they aren't taking any space in memory (performance reasons), so if you try to loop over it, it will give you nothing:

const array = new Array(3); // [empty, empty, empty]
array[0] === undefined; // true
array.map(console.log); // nothing logs 🤦🏻
Enter fullscreen mode Exit fullscreen mode

The arguments in favor of null

When I say that you don't need null, folks that use it a lot (generally coming from other languages that have null as the only nullish value) get pretty mad about such claims. The most common response I get is:

null is for intentional missing values, and undefined should be used when the values were never set in the first place.

The first thing I think with responses like that is: 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". One common usage of null is to do stuff like this:

const people = [
    {
        firstName: "Luke",
        middleName: null,
        lastName: "Shiru",
    },
    {
        firstName: "Barack",
        middleName: "Hussein",
        lastName: "Obama",
    },
];
Enter fullscreen mode Exit fullscreen mode

But you can just omit middleName when the user doesn't have one:

const people = [
    {
        firstName: "Luke",
        lastName: "Shiru",
    },
    // ...
];
Enter fullscreen mode Exit fullscreen mode

And you can set middleName to an empty string if the user intentionally left that blank, if you really need to know that for some reason:

const people = [
    {
        firstName: "Luke",
        middleName: "",
        lastName: "Shiru",
    },
    // ...
];
Enter fullscreen mode Exit fullscreen mode

And the TypeScript representation would be something like this:

type Person = {
    firstName: string;
    middleName?: string;
    lastName: string;
};
Enter fullscreen mode Exit fullscreen mode

Why would we spend memory with a null value there, or bits with a JSON coming from the back-end, when we can just omit what is not there?

But the API is responding with null (maybe written in Java), so I have to use null all over my app as well.

My answer to that is: Use an API wrapper. Instead of "spreading" null all over your codebase, update your surface of contact with the API so nulls are removed, and if you have any contact with the folks making the API, voice your concern of making API responses smaller by getting rid of null values. You should try to avoid ending up over-engineering/over-complicating your app just to deal with null when you can just avoid it altogether.

But in React I use null when I want a component to not render anything

You can use undefined as well.

You have to type 5 more characters when you write undefined explicitly in your code.

Generally, you will rely on it implicitly (omitting the value), but even if we had to type it every time, is worth it compared to all the downsides of null.

Languages without nullish

There are languages out there that don't have nullish values, and instead rely on Maybe, which is a type that means "we might get a certain type or nothing". We can do a simple implementation of that in TypeScript like this:

type Maybe<Type> = Type | undefined;
Enter fullscreen mode Exit fullscreen mode

So we might get whatever type we are expecting or undefined. We can just use ? as well when it's a property or argument:

const aFunction = (optionalArgument?: Type) => // ...
type AnObject = {
    optionalProperty?: Type;
};
Enter fullscreen mode Exit fullscreen mode

To deal with our "Maybes" we can use operators such as nullish coalescing (??) and optional chaining (?.), so...

// We don't need to do something nasty like this:
const greet = name => `Hello, ${name !== null ? name : "Guest"}`;

// We can do this:
const greet = name => `Hello, ${name ?? "Guest"}`;

// Or better yet, because we are using undefined, we can actually...
const greet = (name = "Guest") => `Hello, ${name}`;
Enter fullscreen mode Exit fullscreen mode

Linting like a champ

If you're convinced that null is not a good nullish value, you can avoid it from now on using this great ESLint plugin, and just add this to your linting rules:

{
    "plugins": ["no-null"],
    "rules": {
        "no-null/no-null": "error"
    }
}
Enter fullscreen mode Exit fullscreen mode

Other sources

Here's a list of some sources by other devs that share my opinion about null:

Closing thoughts

My personal opinion about null in JavaScript is "anything written with null can be written with undefined instead", but your mileage might vary, so as usual I close this article with a few open questions: Do you NEED to use null? Don't you have a way of resolving that issue without it?

Thanks for reading this, and special thanks to the 3100+ followers that motivate me to keep doing this series! Remember that if you don't agree with something said here, you can just leave a comment and we can discuss it further.

See you in the next post of this series!

Disclaimer

This series is called "You don't need ...", emphasis on need, meaning that you would be fine without the thing that the post covers. This series explores alternatives, it doesn't impose them, so consider that before glancing over the post and ranting on the comment section. Keep it respectful.

Discussion (122)

Collapse
martinpham profile image
Martin Pham • Edited on

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)

Collapse
romeerez profile image
Roman Kushyn

Wondering how no one still mentioned 'in' operator:

const a = {}
const b = { key: undefined }

console.log('key' in a) // false
console.log('key' in b) // true
Enter fullscreen mode Exit fullscreen mode
Collapse
lukeshiru profile image
Luke Shiru Author • Edited on

Several reasons, I guess:

  1. That would make the argument saying "there is no way of differentiate between intentionally set and unintentionally set" for undefined invalid, because with in you can figure out if it was intentionally set as undefined, or is actually missing from the object.
  2. Because if you set it to null, in will return true as well, so that doesn't make it better than undefined, nor different. So another argument in favor of "they are the same".
  3. Because there are other ways to check if something exists than in.

TL;DR: Because is an argument in favor of the article, I guess I could even update it with that :D

Thread Thread
martinpham profile image
Martin Pham
  1. So you’re saying, with the “in”, when you intentionally set the value to “undefined”, it means the value is “defined”?
Thread Thread
lukeshiru profile image
Luke Shiru Author

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 in you can achieve the same thing with undefined ... 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 🤣

Thread Thread
romeerez profile image
Roman Kushyn

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!

Thread Thread
martinpham profile image
Martin Pham

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

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

Maybe null make life easier when you are not using TypeScript ...

You can use TS without using TS, by typing with JSDocs. Try this out in VSCode:

// @ts-check

/**
 * @typedef User
 * @property {string} id - The user's unique ID.
 * @property {string} name - The user's name.
 * @property {string} [email] - The user's email address (optional).
 */

/**
 * Get a user by ID.
 * @param {string} id 
 * @returns {User}
 */
// Red squiggly here, because we are missing `name`:
// But if you miss `email` is ok because that one is "optional"
const getUser = id => ({ id });
Enter fullscreen mode Exit fullscreen mode

Thanks a lot for that last line, Roman. I greatly appreciate some good vibes in this comment section ❤️

Thread Thread
lukeshiru profile image
Luke Shiru Author

In my experience, consistency is one of the most important thing

If you really think about it, we can have that same consistency with undefined instead of null. At work we have contracts between the API and the client. We translate the type definitions from the back-end in .d.ts files we can use from TypeScript and JavaScript. When something is optional in the back-end, the type is Type?, not Type | null or Type | null | undefined, so we know with confidence that we can send a value or undefined (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 is Type? 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 null and undefined, that convention could use only undefined as well and work ... and this still doesn't negate the fact that you can use undefined everywhere, 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).

Thread Thread
romeerez profile image
Roman Kushyn • Edited on

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

Is a pattern called currying, it allows you to create functions from functions and make everything more reusable:

const update = property => value => object => ({
    ...object,
    [property]: value,
});

const updateName = update("name");
const blankName = updateName("");
const nameFoo = updateName("foo");

const blanks = [
    { id: 1, name: "Luke" },
    { id: 2, name: "Roman" },
].map(blankUser); // Returns the array with `name` on both set to ""

blanks.map(nameFoo); // Returns the array with `name` on both set to "foo"
Enter fullscreen mode Exit fullscreen mode

Maybe is not the best example, but basically you can create updateX and reuse it all you want, with different values for different objects ... compared to doing the same with 3 arguments:

const update = (object, property, value) => ({
    ...object,
    [property]: value,
});

const blanks = [
    { id: 1, name: "Luke" },
    { id: 2, name: "Roman" },
].map(object => update(object, "name", ""));

blanks.map(object => update(object, "name", "foo"));
Enter fullscreen mode Exit fullscreen mode

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.

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.

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.

Screenshot of a experience level set to 7 of 10

Thread Thread
romeerez profile image
Roman Kushyn • Edited on

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

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

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 update function could look something like this:

const update =
    // Generic for the property name
    <Property extends PropertyKey>(property: Property) =>
    // Generic for the value
    <Value>(value: Value) =>
    // We expect the "Source" object to be an object of unknown properties
    <Source extends Record<PropertyKey, unknown>>(
        object: Source,
    // We return that Source combined with the new property and value
    ): Source & { [property in Property]: Value } => ({
        ...object,
        [property]: value,
    });
Enter fullscreen mode Exit fullscreen mode

I do love to do TS + FP, so creating an article about this might be interesting for other folks as well 😄

Thread Thread
romeerez profile image
Roman Kushyn

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.

Collapse
martinpham profile image
Martin Pham

You don’t need “in” /s

Thread Thread
lukeshiru profile image
Luke Shiru Author

lol

Collapse
martinpham profile image
Martin Pham

Btw it’s fun when

  • Access a variable which is not defined: we have ReferenceError
  • Access an attribute which is not defined: no error, it returns undefined
Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

Yup, it kinda sucks, but is preferable to return undefined for something that isn't defined ... you can still check if a variable exists before trying to access it, by using typeof:

console.log(foo); // ReferenceError
console.log(typeof foo !== "undefined" ? foo : "Nope"); // "Nope" 🎉
Enter fullscreen mode Exit fullscreen mode

Accessing properties is a better DX, and with ?. and ?? is even better:

const obj = {};
console.log(obj?.foo ?? "Nope"); // "Nope" 🎉
Enter fullscreen mode Exit fullscreen mode
Thread Thread
martinpham profile image
Martin Pham

I’m not talking about the check. I’m just trying to point the inconsistency between two cases about the same thing.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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.

Collapse
patroza profile image
Patrick Roza • Edited on

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

Collapse
martinpham profile image
Martin Pham

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

Thread Thread
lukeshiru profile image
Luke Shiru Author

That depends on the API implementation. This is like saying:

You need to use null because I wrote if (value === null)

Which is kinda like a Straw Man. If the API has a poor implementation that requires null, that doesn't mean you need to use null in your entire codebase, that just means you need to use it in your surface of contact with said API.

Thread Thread
martinpham profile image
Martin Pham

Sorry but I wouldn’t consider an api which has consistent response for nullable object, as a poor implemented api..

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 🎉

Thread Thread
patroza profile image
Patrick Roza

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.

Thread Thread
martinpham profile image
Martin Pham

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

"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 null X_X

Collapse
lukeshiru profile image
Luke Shiru Author • Edited on

With tech like TS, contracts are actually pretty easy to define. There are tools out there that turn all kind of type definitions in .d.ts files, 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's Type | undefined, so you can omit that key or send that Type and 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 🎉

Collapse
lukeshiru profile image
Luke Shiru Author
  1. Yes it does, when you skip a value, tada! That's undefined 😉
  2. Because JS folks decided to do so. Other languages that only have a single nullish value use that for any scenario that requires nullish values. From JS we can also just use one, and what I say in this article is that you can just use undefined.
Collapse
martinpham profile image
Martin Pham
  1. As I replied to other guy. The response should be consistent, if you skip this value, tada! an error should be thrown. Because you won’t know if the value is missing (eg: it has, but api fails to give it) or the value is not defined.
  2. Other languages which don’t have it because they are more strict, where it’s bad when you try to use an undefined variable. JS was born as a mess, but growing up as a flexible language, that’s why we have undefined and null to use together
Thread Thread
lukeshiru profile image
Luke Shiru Author
  1. If the API fails, you should handle that in other ways (response headers and proper error handling), if the API responded with the wrong body, that will be a problem either if you use null or undefined, so I still don't see much value for null in this scenario. If a value can either come or not, then you can define it as optional (meaning Type | undefined), and in the front end you can just do value ?? "default value" or if (value === undefined) { throw // ... } or whatever 😄
  2. You should take a look at optional chaining and nullish coalescing, saying "it's bad when you try to use an undefined variable" feels like you might not be aware of this operators, but long story short, it's no longer bad. You can even "call" undefined 🎉
Thread Thread
martinpham profile image
Martin Pham
  1. Of course you can handle it anyway, but how you know if the value is missing (an error), or if the value is not set?
  2. I was saying that about other languages, not js
Collapse
omgimalexis profile image
Alexis Tyler

“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.

Collapse
lukeshiru profile image
Luke Shiru Author • Edited on

When a value is "optional", that only means it can be defined or not, it can be a type or be nullish (the Maybe type I mentioned). So something that's optional has the shape Type | undefined, or Type?

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 | undefined or string?. So if the user set it to be empty intentionally, then you can set the value to "", and if not then set it to undefined.

You can then send it like this if the user left it blank intentionally to the back-end:

{ "userId": 1, "name": "" }
Enter fullscreen mode Exit fullscreen mode

And if the user didn't set it at all, then:

{ "userId": 1 }
Enter fullscreen mode Exit fullscreen mode

Trust me, every piece of code written using null in JavaScript/TypeScript, can be written without null, and it generally ends up being cleaner.

Collapse
martinpham profile image
Martin Pham

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

Why?! If it doesn't have extra_topping then 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.

Thread Thread
martinpham profile image
Martin Pham

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

Thread Thread
lukeshiru profile image
Luke Shiru Author

So much mentions of pizza are making me hungry 🤣 ... Answering:

  • If the pizza I ordered comes with some toppings and I didn't said anything, the pizzeria will send their default.
  • If I make clarifications about the toppings, then they'll update the pizza accordingly.

Do you say "keep the default toppings" every time you order?

Thread Thread
martinpham profile image
Martin Pham • Edited on

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)

Thread Thread
lukeshiru profile image
Luke Shiru Author

Did you ever actually bough a pizza? 🤣

  1. Because you didn't asked for any? You don't need to say "I don't want any extra toppings" every time you buy a pizza, you just need to clarify when you want them. Even in Doordash the extra toppings is an optional field, that you can just omit, you don't need to go into it and select "I don't want extra toppings", that's the "default".
  2. That's your problem as a client. You should know that there's an "extra toppings" option, and ask for it if you want them, or omit them if not. If I forget to buy a Pepsi to drink with my pizza and I was thirsty, is not a problem with the pizzeria, is my problem as a client.

Maybe this is not the best analogy for your point about needing 2 different nullish values?

Thread Thread
martinpham profile image
Martin Pham • Edited on

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..

Collapse
omgimalexis profile image
Alexis Tyler

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)){}

Thread Thread
lukeshiru profile image
Luke Shiru Author

Using falsy values is like pointing a gun directly to your foot ...

const { option } = something;

console.log(
    `Option: ${
        option === "" ? "Intentionally unset" : option ?? "Not set yet"
    }`,
);
Enter fullscreen mode Exit fullscreen mode

And you'll get:

  • Option: Intentionally unset if the value is "".
  • Option: foo if the value is "foo".
  • Option: Not set yet if the value is undefined.

Really, you don't need null, or at least not for this.

Thread Thread
omgimalexis profile image
Alexis Tyler

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

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 | null and rely on falsy, that only means that if you have different types, you need to deal with them in different ways. Using null always 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".

Thread Thread
darkwiiplayer profile image
DarkWiiPlayer

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.

Thread Thread
scottshipp profile image
scottshipp

If other languages can work without two different type of nullish values, why JavaScript can't?

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 null which 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 returns null when client code gets a value from the map using a key that the map doesn't know about. Unfortunately, it's well-known that null is 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 undefined rather than null which disambiguates the cases.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 Maybe instead 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_X

Thread Thread
scottshipp profile image
scottshipp

You mean to say...

What 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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 using null and avoiding undefined, but you end up doing extra work when you could just use the nullish that the language uses.

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.

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 undefined and makes you think: You need null for that?

Thread Thread
darkwiiplayer profile image
DarkWiiPlayer

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 FalseySymbol counterpart to Symbol.

Thread Thread
scottshipp profile image
scottshipp

If you want an actual reason not to have two non-values, it's that this just seems very arbitrary.

I don't think it seems arbitrary at all. The language specification is not arbitrary when it describes null as meaning something explicitly set and undefined meaning 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 both null and undefined, they both have a clear unambiguous meaning and plenty of use cases.

Thread Thread
darkwiiplayer profile image
DarkWiiPlayer

You misunderstood my point; both null and undefined are 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 null and undefined.

As I said, at that point a more elegant solution would be adding FalseySymbol or NullishSymbol or both and leave it up for the user to define specific non-values that hold whatever semantic meaning they want.

Collapse
darkwiiplayer profile image
DarkWiiPlayer

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

let no_selection = Symbol("User did not make a selection") // or just undefined
let undisclosed = Symbol("User prefers not to share this information")
Enter fullscreen mode Exit fullscreen mode
Collapse
matyasjay profile image
Matyas Angyal

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!

Collapse
darkwiiplayer profile image
DarkWiiPlayer
typeof null
// 'object'
Object.keys(null)
// Uncaught TypeError: Cannot convert undefined or null to object
Enter fullscreen mode Exit fullscreen mode

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 null is an object, then it should behave like one.

Collapse
lukeshiru profile image
Luke Shiru Author

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.

Collapse
matyasjay profile image
Matyas Angyal

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 null over undefined when I want to consider an asset to be explicitly empty (but still defined in the scope of the current code). undefined is 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 global window in Node environment). It's all about practices and personal preference. In my opinion, null is a handy stuff and I'm happy to use it.

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

It's all about practices and personal preference

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 undefined introducing bugs, the vast majority of them are solved with the good ?? and ?., so you can actually do stuff like:

const obj = {};

obj?.level1?.level2; // undefined, no errors
obj?.method?.(); // undefined, no errors

obj?.level1?.level2 ?? "default value"; // "default value", no errors
obj?.method?.() ?? 10; // 10, no errors
Enter fullscreen mode Exit fullscreen mode
Thread Thread
matyasjay profile image
Matyas Angyal

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 ❤️

Collapse
leob profile image
leob • Edited on

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).

Collapse
lukeshiru profile image
Luke Shiru Author

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 withnull as 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 😅

Collapse
leob profile image
leob

People should remain rational, getting all emotional over tech debates is a bit stupid lol

Collapse
scottshipp profile image
scottshipp

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.

Collapse
lukeshiru profile image
Luke Shiru Author

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.

Collapse
scottshipp profile image
scottshipp

You don't "need" stoplights but not knowing about and incorporating them into your driving will have disastrous results.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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.

Thread Thread
scottshipp profile image
scottshipp

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).

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

Might be a language barrier that's complicating this conversation, but I said this already several times in the comments: You should keep null in the surface of contact with things that "need" null, like APIs. That means that you'll have to write null to interact with those pieces of code that didn't figured out how to work with undefined, yes ... but still doesn't mean you need to write null in the code that doesn't interact with those APIs.

[your code] <-> [API methods/functions] <-> [API that requires null]
Enter fullscreen mode Exit fullscreen mode

You can keep null in the block in the middle there, only. And in your code just use undefined. 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?

Thread Thread
scottshipp profile image
scottshipp

You should keep null in the surface of contact with things that "need" null

It sounds nice, but null is part of the JavaScript language itself. It has an explicit meaning in the specification:

primitive value that represents the intentional absence of any object value

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 null is going to be untenable, and, even if you don't share, your JS is not going to be idiomatic.

Thread Thread
lukeshiru profile image
Luke Shiru Author

I mean, I worked for years now with different teams without using null, and nobody misses it or says "we could solve this with null". 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.

Thread Thread
scottshipp profile image
scottshipp

I mean, I worked for years now with different teams without using null, and nobody misses it or says "we could solve this with null".

That doesn't mean it's idiomatic.

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.

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 null to make that distinction between two "optional" values, but you don't need null for 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 null or you do that unnecessary distinction between null and undefined, but the idea is to make you actually think if you need that distinction, or not. Have you actually tried to code without using null yourself? Maybe you already did, and you missed null every single day, but my experience (and the experience of many other folks) was the exact opposite.

Thread Thread
scottshipp profile image
scottshipp

You keep saying is not idiomatic, and my point is that you're just saying "the same thing with two different words".

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.

The point of the article is to actually make you think if you need it

These reasons to use null seem compelling to me:

  • null is core to the JavaScript language--it's in the language specification and used in various language features
  • null is returned from data sources, user input, and third-party libraries and API's whether or not your code uses it
  • As a result, null is idiomatic JavaScript (leading to the point above)
  • null has a different meaning from undefined, which is significant, and the two cannot be used as substitutes of each other without creating ambiguity (null is used to show that the value was explicitly set, undefined is used to indicate that no value was ever set)
Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

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.

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 null value, for example, and my function returns undefined instead ... but the same could happen the other way around :/ (not to mention both are solved the exact same way)

null is core to the JavaScript language--it's in the language specification and used in various language features

Being in the language is not a "compelling" reason, because we avoid things in the language all the time that we know are bad. eval and with are still part of core JavaScript, and you wouldn't use those because you know that they lead to issues of all kinds.

null is returned from data sources, user input, and third-party libraries and API's whether or not your code uses it.

I already said that you can then deal with null only when working with those. Not to mention that data sources can also return undefined, it depends on how are they made, and the actual user input is never null, you can get "null" of user input only if the handler for said input returns null.

As a result, null is idiomatic JavaScript (leading to the point above)

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.

null has a different meaning from undefined, which is significant, and the two cannot be used as substitutes of each other without creating ambiguity (null is used to show that the value was explicitly set, undefined is used to indicate that no value was ever set)

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?

Thread Thread
scottshipp profile image
scottshipp

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 null value, for example, and my function returns undefined instead ...

Exactly. Consider the matches() method on string. It returns null to indicate that there are no matches found. Let's say we take your approach and wrap this in our own method where we return undefined for this case instead. Now you have a client of your method who is a JavaScript developer. They check for null to see if no matches were returned, but you have overwritten this with undefined. 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.

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

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 both null and undefined (again, you handle both cases the same way). So you can do something like this:

const [match] = "bar".match(/foo/) ?? [];

console.log(match ?? `Not found`);
Enter fullscreen mode Exit fullscreen mode

Which works with both null and undefined. Instead of having to write:

const matches = "bar".match(/foo/);

console.log(matches !== null && matches[0] !== undefined ? matches[0] : `Not found`);
// or relying on falsy
console.log(matches && matches[0] ? matches[0] : `Not found`);
Enter fullscreen mode Exit fullscreen mode

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. 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:

const match => regExp => string => string.match(regExp) ?? [];
Enter fullscreen mode Exit fullscreen mode

This way you can not only curry it, but always be sure you'll receive an ArrayLike from it instead of RegExpMatchArray | null which is far from ideal.

Thread Thread
scottshipp profile image
scottshipp

Do you actually do if (matches === null) nowadays?

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 matches returning null (and not undefined) in probably a few dozen different ways. And when/if you change that, you introduce the potential for bugs, as previously pointed out.

You don't need a wrapper.

The idea of a "wrapper" came from your comments, not mine, though.

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.

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 both null and undefined consistent with general practice. It is the standard practice (the idiom) in JavaScript for null to represent a purposefully-set value and undefined to represent a value that was never set, and this is in the language specification as well as the canonical books and documentation.

Thread Thread
lukeshiru profile image
Luke Shiru Author

It seems that you asking this question proves the point.

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 do matches === null, but is good for you to assume that the function will return null instead of actually checking the return type of the thing you're using?

And when/if you change that, you introduce the potential for bugs, as previously pointed out.

But that's the thing, if you use a function assuming it will return either null or undefined, 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 return undefined, and instead you decide that it will return null, 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.

I am not sure but I'll give the benefit of the doubt here and assume that you are not willfully missing the point.

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 null in 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 if null is so necessary, how is it that Angular is considering dropping null, TypeScript doesn't use null in its source, and folks like Douglas Crockford, with years of experience in JS, don't use null at all and just use undefined instead?

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 null exist 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 (eval and with are 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 null at any point? Do you ever worked with a library/framework or codebase without null and 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.

Collapse
vladivo profile image
vladivo • Edited on

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.

Collapse
lukeshiru profile image
Luke Shiru Author

You usually don't want to write things like typeof string && ""

That doesn't make much sense for me. If something can be either string | undefined, then adding | null to 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:

Object.entries({
    number: null,
    boolean: null,
    string: null,
}).map(validate);

Object.entries({
    number: undefined,
    boolean: undefined,
    string: undefined,
}).map(validate);
Enter fullscreen mode Exit fullscreen mode

So validate there could be implemented like this:

const validate = ([key, value]) => `${key} is ${value ?? "nullish"}`;
Enter fullscreen mode Exit fullscreen mode

Which would work exactly the same for both null and undefined. What's the advantage of null over undefined here?

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 use null all over your app as well. Not to mention that if a non-string field is empty, it could be undefined as well, same if you reset it, is just an implementation detail of that forms module, which doesn't mean you need to use null in places where you're not interacting with that particular module.

Collapse
vladivo profile image
vladivo

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.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 null and you can use undefined to express the same things better, but if there's a library that only works with null, then you have to use it there. The main idea is that we shouldn't default to null and just use it when is actually necessary. Some folks think is necessary to make a distinction between null and undefined and 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 use null", that only means "you should use it when is actually necessary", like APIs or libs that only work with null.

About any vs unknown, I might write a "you don't need any" showing how you can use unknown everywhere, safely, even if you want to be lazy as you'll be using just any ☺️

Thread Thread
vladivo profile image
vladivo

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>.

Thread Thread
lukeshiru profile image
Luke Shiru Author

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 having any and accidentally calling a method or passing it to a function without first checking what that is. I see unknown as strict any, 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 null at all (unless, as we said already, I'm interacting with something that needs null). And I share that experience you had, of migrating codebases to only using undefined, or having that no-null ESLint rule, and the best thing about that experience is that not only me, but nobody in those teams missed null (that's also why I shared that talk given by Douglas Crockford, because he had the same experience, years ago).

Thread Thread
vladivo profile image
vladivo

You're right. And I am stupid. Of course, unknown can be narrowed same as any. Simply forgot that.

Thread Thread
lukeshiru profile image
Luke Shiru Author

You're not stupid! I used to use any quite a lot before unknown was introduced, so it's ok if you're still checking before using it. The main difference is that unknown forces you to do that check, but if you still do it is ok!

Thread Thread
vladivo profile image
vladivo

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... 🤷😄

Thread Thread
lukeshiru profile image
Luke Shiru Author • Edited on

That's what happen to folks like us that were in this TypeScript game for a long time x'D

Collapse
mistval profile image
Randall

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 === null or such).

Collapse
inhuofficial profile image
InHuOfficial • Edited on

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! 😜🤣

Collapse
lukeshiru profile image
Luke Shiru Author