Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Intent to stop using 'null' in my JS code (github.com/sindresorhus)
72 points by anuila on Oct 31, 2020 | hide | past | favorite | 136 comments


I agree with many folks here on HN in that `null` and `undefined` have important differences. `undefined` appears when you access an invalid field, while `null` appears when you access a _valid_, but empty field. This information comes in handy during debugging. If your code is trying to access a field on `undefined`, then you may well have a typo. If your code is trying to access a field on `null`, then you've simply made some wrong assumptions about your inputs. The next steps for those two scenarios are quite different!

Arguing that all `null`s should be replaced with `undefined`s in JS code also kind of implies to me that JSON should replace the `null` keyword with `undefined`, which just sounds crazy to me. Why store "undefined" values in serialized data? If it's not defined, don't define it... `null` allows you to convey that "this value may exist, but currently doesn't".


In conventional JS, undefined is used for both and null is just a curiosity. JS does make a distinction between a non-existent field and a field containing undefined as a value. Is there anywhere where the JS standard library uses null? I don't recall any. In my experience having two null values causes problems, for instance you check for one but not the other. It also remains the one remaining use of the == operator, without null we can expunge == too.


> Is there anywhere where the JS standard library uses null?

Plenty, though it depends a bit on what you count as JS standard library.

An object’s prototype may be null, so things like Object.getPrototypeOf(), Object.setPrototypeOf(), Object.create() and .__proto__ all deal with null.

Anything that is accessing a named property that exists on a class will yield null for no value, never undefined.

This is seen in DOM methods; things like Node.firstChild and Node.parentNode spring to mind as potentially null, and in each case it would be semantically quite wrong for them to be undefined—they are defined, as “no value”.


while I agree with your position the criticism of null can also be reformulated as: are there places in common usages where both null and undefined are common/reasonable values and have different meaning?

conceptually I think so, but I am not sure how much it actually helps with the language ergonomic.

in hindsight I think undefined should have been more powerful, eg setting a property to unrefined should effectively remove the property from an object (which would also affect the prototype chain)


Not sure I agree that there is some logical reason why Node.firstChild should be null for an empty element whereas e.g. array[0] should be undefined for an empty array. I think the actual reason is just that DOM is an old API. If designed today it would return undefined.


> If designed today it would return undefined.

Absolutely not.

Null means it knows what you’re asking for, and there’s no value.

Undefined means it doesn’t know what you’re asking for. (This includes function calls that don’t return a value, since you’re asking for a value that wasn’t produced—again different from null, which is explicitly producing a value.)

Node.firstChild being null for a node with no children is absolutely correct. Undefined would be incorrect.

Seeking past the end of an array finds elements that have not been defined, so undefined is correct and null would be incorrect.


Except in these cases, undefined still can make complete sense if you're willing to rephrase things (like sindresorhus is).

For your Node.firstChild example, one could say that if a node has no children, then it's firstChild is not defined as it doesn't exist, and so undefined makes sense. You can construct similar plausible arguments for other usages of null, though you cannot do the inverse.


Node.firstChild producing null is this conversation:

“Hey node, what’s your first child?” “I don’t have a first child.”

Node.firstChild producing undefined is this conversation:

“Hey node, what’s your first child?” “Huh? What’s a first child? I have no idea what you’re talking about.”


Node.firstChild _is_ defined though, it's just empty. Hence null.

An undefined property would be one like Node.helloWorld, which doesn't not exist at all on the object.


Undefined occurs for example when you don’t pass a parameter, or when a function doesn’t return a value (but you read/store it anyway). Null comes for example from JSON, from nullable SQL columns, and it is passed explicitly to say „this value is empty“ very commonly in libraries.


document.querySelector(selector_with_no_matching_elements) and other similar select methods that 1. Will only yield a single element at most (never an array) 2. Might come up empty.


Agreed, however we look at it there is no way to change the several dozen JSON libraries out there.

People should avoid using undefined, not avoid using null.


Avoiding undefined is more preferable to me, since then it only shows when when you screw up.


[flagged]


one can argue that if you obtain a value in such a way you should validate it first or not use it at all


In general I would validate for the thing I'm actually looking for instead of validating against undefined and other invalid input values. For example:

    function setContrast(value) {
        if(typeof(value) !== "number") throw "input must be a number";
        if(!(value >= 0 && value <= 1)) throw "input must be between 0 and 1 inclusive";

        // ... rest of code ...
    }
This pattern will generally avoid any issues with undefined values being passed in.


Furthermore, if the function returns `undefined`, as in "it's not expected to return a value", you should probably not try to assign its return value to anything you'll see again.


+1. I use null in js as the equivalent of "This page intentionally left blank."


In JS, undefined is also typical for uninitialized fields and variables, so undefined can show up without a typo. And if you've got typos, why not use e.g. TypeScript?


is there a way to use the typescript type checker on js files using jsdoc types?


Yes. Simply install TS and make a jsconfig.json file, Vscode will pick it up automatically.


You need to add

    // @ts-check
at the top of the file: https://www.typescriptlang.org/docs/handbook/intro-to-js-ts....


There is a config setting for enabling it globally now

https://www.typescriptlang.org/tsconfig#checkJs


I was missing this, thank you.


i think of null as conveying:

"this value was intentionally set to 'null'

in contrast to undefined:

'this value is undefined (e.g. was never [correctly] set)'

Though this doesn't apply to dealing with unsanitized json parsing.


There's still a distinction between explicitly assigned undefined, and deleted or not assigned field. So, two types of undefined in addition to null, if you will.


Yes. This is annoying / I wish it weren't part of the language. It's an easy footgun, since `object.property === undefined` does not disambiguate between the two cases. Similarly, I wish `typeof null === "null"`.


The underlying problem why this isn't clear to pure JS developers is that even the native APIs do this differently, but for various reasons.

For example, take Array.prototype.find(). Would be nice to have null here as a default return value. But what about Arrays containing null as a value? What about the case of null || false? What about the case of false || null?

There were a lot of design discussions to come up with those APIs...but I agree with you that technically, undefined should not exist. I'd rather have a syntax error being thrown than having to deal with undefined.

As undefined is specified as a hackaround as a global property, ... most of the implications are just a workaround, too.


I see the logic being that the value hasn't yet been defined so we're going to articulate it as such.

A failing of JS lang that so many of these discussions have to keep happening.


The key is defined but the value is not?


yessir, the key is declared


The author makes compelling arguments but I respectfully disagree with their conclusion. Null exists for cases where you want to set the the absence of a value intentionally. It is literally the value of having no value. Undefined exists for cases where no value has yet been set. It's a subtle but important difference and allows a certain amount of nuance and expressiveness in ones code to communicate intent. I do think it's fair to say that a lot of devs don't use them that way. They _are_ for separate use cases though.


In functional programming the Option type exists to make the 'literally no value' case more explicit.

https://en.m.wikipedia.org/wiki/Option_type

If you are using TypeScript (TFA mentions it), fp-ts provides a good functional programming layer. Although if you aren't already familiar with functional programming it's going to be hard as the documentation is rather lacking.

https://github.com/gcanti/fp-ts/blob/master/src/Option.ts


The Option type isn’t really something owned by functional programming. Union types exist just fine in imperative and even declarative programming.


The option type also prevents you from accessing the value if it's absent. Something a C union cannot do. Is there any example of options in an imperative language that would predate ML (which AFAIK introduced Some/None as the variants of option)?


C can’t do that. But imperative programming can. Nothing about type based pattern matching is constrained to FP.


Not that I'm disagreeing with you in any way, but it's worth recognizing that imperative languages typically do pattern matching and type constraining in a declarative manner. So one could make an argument that what you're saying is only true because imperative languages are not forbidden from employing FP. If a language uses declarative constructs, then it's not a wholly imperative language.


I really don't think that FP should be able to take claim for this. Or, at the very least, I don't think the sort of evangelical FP should. Type pattern matching isn't all that different from virtual dispatch if you squint.

If the FP community wants to say that impure languages have been "doing FP" for decades then great! The religious war is over! We can happily have mixed paradigm languages and move on. But that's not what I'm seeing.


And the magic sauce that makes Option nice to use is the type checker that can force you to handle the None case.

I would go even further that even if the semantics are a little clunky (wrap/unwrap) any programming language can implement Option.


True but you can’t restrict the type if your language is dynamic.


I like (explicit) nullability as syntactic sugar over Option/Maybe. Kotlin gets this right.


fp-ts had many niceties in it, but in Typescript, types are already non-nullanle by default; is there any reason to use Option<string> over string|null? You'd have the same exact type safety either way.


It's a bit high level in that the benefits aren't obvious until you buy into other functional concepts, but basically it's that Options can be layered i.e. Option<Option<string>> is possible. If you just use ordinary union types there's no way to express that.

As an aside, TypeScript is very unsafe by default, and still relatively unsafe with strict mode enabled. Stringent fp-ts usage bypasses these issues.


> still relatively unsafe with strict mode enabled

Is it? I have been using Typescript more and more these last few years, and lately haven't really experienced any issues where it's type system failed me.

To clarify, I'm not trying to argue against your point, genuinely curious to learn if fp-ts can really improve my code quality in production.


It is, for example accessing an array or record doesn't unionise the returned value type with undefined, though this particular gaping hole of unsafety is being addressed in 4.1 with a new flag.

Beyond that there's functions that throw/reject doing so outside the type system, something solved by Either/Option/similar types in fp-ts.

The biggest benefit of fp-ts though is that it makes you think functionally. If you're dead set against that, then there are plenty of other libraries that give you types/wrappers like these without having to use pipe/flow (two functions you'll want to use before anything else in fp-ts).


> Null exists for cases where you want to set the the absence of a value intentionally.

Only in Javascript though. In Typescript this would be clear from the fact that the type has the property.

Of course Typescript is not Javascript, but nobody should use plain JS anymore.


What if you want your property to have a default value, such as null?


If you want your property to have a default value, give it an actual default value, not the explicit absence of one (kind of defeats the point to set a default value of null).


It doesn't defeat the purpose of a default/initial value because it shows that it should be filled.

When I'm using hooks in React this is very common.

You must give an initial value, and at some point in the future you will update it.

By setting the value as null initially I can easily check whether the variable has been filled or not. This is most important for initially rendering the UI.


I think the parent comment was saying that you could always make that explicit.

> By setting the value as null initially I can easily check whether the variable has been filled or not

I don't think there's anything wrong with this but it does mean your variable can be in two states~, one where "it shows it should be filled" and one where the value is present and can be used.

You can use null for this if you want and most programmers will understand these states. You can also choose to make this even more explicit by creating a type that expresses this.

example in Kotlin but you get the idea (applies to any variable in any language)

    sealed class ThatVariableYouCareAbout {
     object ShouldBeFilled: ThatVariableYouCareAbout()
     data class HasBeenFilled(val x: String): ThatVariableYouCareAbout()
    }

    // usage
    fun test(thatVar: ThatVariableYouCareAbout) {
      if(thatVar is ShouldBeFilled) {
         //
      } else if(thatVar is HasBeenFilled) {

      }
    }
Seems overly verbose but that's the reality of the variable you're talking about. This represents those states you described. `null` is a crutch in most languages for representing this state and the cause of a lot of subtle bugs (people use it to reset/clear out values too which in reality could represent a different state depending on what you _really_ want the variable to represent).

All that being said, null is generally understood to have this meaning~ and I have/do use it but with an understanding that it's of lazyness/not wanting to be overly verbose (language consideration).

The case you are describing is very familiar to me (rendering UI with initial states through a framework like react) and having an explicit type for the initial state is the ideal, or at least the most explicit, solution.

Option works for 2-state variables like this


null is not a very good default value in most cases though, since the operations you do with values of "real" types will not work on null. Therefore actually using your "default" value is pretty much guaranteed to be a bug, identical to using an uninitialized variable.

It would be better to just error out and not allow the uninitialized usage at all, but unfortunately that's not possible in Javascript.


That's the point though. I can easily do a null check to handle whether or not to process/use the value.

For instance, if I'm fetching some data I usually want to initialize the data variable as null for a couple of reasons.

1) null is not truthy, empty arrays and objects are

2) It shows that the variable is supposed to be filled, unlike undefined


This is problematic in that you don't control external APIs. For example look at the DOM which preferences null but returns undefined when there is a language construct in the way:

    document.getElementById("asdf") // returns null;
    document.getElementsByTagName("asdf") // returns [];
    document.getEleemntsByTagName("asdf")[0] // returns undefined
    document.getElementsByTagName("body")[0].getAttribute("asdf") // returns null
    //
    document.getElementsByTagName("input") // assuming not empty for the next several lines
    document.getElementsByTagName("input")[0] // Element object
    document.getEleemntsByTagName("input")[0].value // returns "" as valus is implicitly an empty string if not supplied
    document.getElementsByTagName("div")[0].value // returns undefined even though the element is present but expresses an inference to an unsupported property
I completely agree supporting both null and undefined complicates some forms of validation, but I have accounted for this in my test automation. I don't encounter this problem with input validation or managing event handlers.

Here are the rules I use:

* undefined is a value where a value is never before assigned. Yes, you can do stupid things in opposition to the default such as arbitrarily assigning undefined or even redefining undefined to an assignment, but those footguns are not be accident.

* null is a value that is explicitly assigned.

If you program with those assumptions in mind you are safe 99% of the time given default language constructs, which doesn't account for the intentionally bad decisions other people make. This also helps with troubleshooting because when an error is encountered due to a null or undefined value the assignment distinction helps to diagnose the problem.


> document.getElementById("asdf")

> document.getElementsByTagName

For what it's worth I usually wrap these functions to e.g. throw an error if they don't find any items as that's what you usually want to happen but forget to write checks for. It's rare I'll use getElementById and expect it not to find something.

Lots of undefined + null values crop up from situations like this where you want to fail when something you expect to be there isn't found e.g. file handling, dictionaries, web requests.

It's a no-brainer to use TypeScript as well if you want robustness from these kinds of bugs, where returning undefined or a value is more practical because TypeScript will force you to check for undefined. I don't understand why anyone would willingly give up to type checks like these as it's obvious to me you can't compete with automated checking.


I actually find all of the return values reasonable.

As you said,

undefined == I just don’t know what the value is

null == I know that there isn’t supposed to be a value here.


Why is this distinction important practically?


Because they mean very different things. E.g. "no-one owns this land" is very different from "I don't know who owns this land". "You don't need to do anything" is very different from "I don't know what you need to do". "No-one's in that room" is very different from "I don't know who's in that room". Of course the practical difference depends on context, but so does the practical difference between 2 and 3.


That’s not what I asked. I know what the distinction is. I asked why would I ever need that distinction in a program.

In 10 years I’ve never come across a situation where I found that distinction useful.


Agree. I would never attempt to write flow control that had different cases for handling undefined and null. It feels so extremely brittle


Undefined should mean in most cases that you've made a programming mistake, be that a typo or an invalid assumption about the type of thing you're getting back.

Null should mean in most cases that the data you wanted to access was invalid, but you can expect it to exist there in other circumstances.

Neither are a guarantee in JS, and you can find counterexamples for the counterexamples of the counterexamples, but that's the intention behind each.


In the past it was one of the ways to distinguish between arguments to function that were not passed in by the caller.


It shouldn’t be but due to the way JavaScript is structured it is.


  const w = (fn, _) => function(...args){
    return (_ = fn(...args)) === null ? undefined : _;
  };
  document.getElementById = w(document.getElementById.bind(document));


I seriously hope this is joke code.

Returning undefined for null, as well as changing dom functions is a great way to ensure unintended side effects will creep into your code.


It is an illustration of a concept. I wouldn't call it a joke, but I also wouldn't use it.


For sure you wouldn’t actually commit this, but it’s for sure interesting to do the mental gymnastics to understand it.


undefined would be rare

    foo
    //Uncaught ReferenceError: foo is not defined
if not property access, and it lies

    o = { foo: undefined }
    o.foo 
    //undefined
    o.hasOwnProperty('foo')
    //true
Array is an object, access by index is property access

    a = []
    a[0]
    //undefined
That's unfortunate. In Ruby:

    foo
    #NameError (undefined local variable or method `foo' for main:Object)
    defined?(foo)
    #=> nil

    o = Object.new
    o.foo
    #NoMethodError (undefined method `foo' for #<Object:0x000055f2f4e56e78>)

    a = []
    a[0]
    #=> nil
And Ruby enforces method arity and keyword parameters:

    def foo(value)
    end
    foo
    #ArgumentError (wrong number of arguments (given 0, expected 1))
    
    def bar(value:)
    end
    bar
    #ArgumentError (missing keyword: :value)
Basically it throws on undefined.

I think reimplementation is a great way to uncover how things work, Ruby "fix" for property access

http://sergeykish.com/ruby-like-javascript-undefined.rb

Anyone knows how to relax arity?


If the value is expected to be a Boolean, use undefined to set it as undefined.

For everything else, use false to “explicitly assign it”, you don’t need a third type.


> use false to “explicitly assign it”

Then you still have null, you’re just spelling it funny while confusing all the other developers who have to deal with your code and making it incompatible with language constructs like the nullish coalescing operator.

If the concept you are trying to express is “null”, then please use null to represent that instead of bringing in a poor substitute like an overloaded false.


No, most likely I'll just use undefined because my abstractions are not a complicated mess that need 2 null types.

Found? string

Not found? undefined


> most likely I'll just use undefined

You were telling people to use false and that’s what I was responding to. That’s why I quoted that part in my comment.


While I agree the presence of both null and undefined in JavaScript can make things a big pain in the ass, trying to get rid of null in favor of undefined is a huge mistake.

For example, a couple years ago GraphQL specifically added support for the difference between undefined and null, and it was a good change. An optional field on an input type can be left off (in which case it is undefined) or it can be explicitly set to null. For a mutation, these have very different meanings: undefined means "don't update the property" while null means "update the property to null (which usually means 'delete')". These are very different things.

This thread, https://github.com/graphql/graphql-js/issues/133, is a good overview.


> null means "update the property to null (which usually means 'delete')".

That is pretty humorous, null means make it undefined, so I guess false will need to mean make it null, and true can mean make it false?

The general problem is trying to get enough metadata directly into the language to manipulate it's own programs with such metadata. Of course this never works it just goes to an edge case so absurd that absurd syntax stays reserved for things with meta operations so they can work on most things yet not themselves or each other.


I don't think this is an absurd edge case at all, and your initial paragraph doesn't make sense to me.

Let's say I have an object that represents an object in the DB, and one of the properties on that object represents an relationship to another entity which may not always be there. Then setting null on the object would mean set the corresponding DB column to null. How is that a discrepancy?

The problem with JS isn't that it doesn't really need null, it's that it doesn't really need undefined, but now that undefined is there it's impossible to rip it out. Most other languages that have null also have a separate concept of "doesn't exist": accessing a non-existent property would throw an exception, not return null, and for example you could tell the difference between whether a Map has a value at all vs has a null value by calling the equivalent of map.containsKey. Point is it is often necessary to tell the difference between "object doesn't have a property" vs. "property is null", and for better or worse (I think mostly worse) JS uses undefined to distinguish between those cases.


Assume your graphQL use case is working with stored graphQL operations. Once you add this special meaning of null, null is no longer infrequent in the data itself, so using null to represent delete is a good idea on the existing data and then a bad idea once implemented (is this an attempt to delete a part of a query or is the query a delete query, or both?)

A new patch must follow, etc. Homoiconic languages and channel protocols are crafted to avoid this kind of meta rat races with their own representation.

null is actually the null object, so if an object is not expected the use of null is wrong, a key should exist but it's value is undefined. AFAIR even most DOM operations are correct in this distinction.

undefined as a value does not mess with Object.keys or hasOwnKey.. it truly is what it claims to be and is truly distinct from a never existing value in JS.

JSON is buggy, but we knew that. Trying to get metadata on the data channel instead of the opposite is buggy, but we knew that too.

Better things exist to implement a proper interface today, i.e. one can use Symbol to distinguish current runtime metadata that truly can not collide with the data.


TBH I can't follow the point you're trying to make, at all.


TLDR, I view this as the metadata treadmill:

A> Using X to mean `delete` is using up X such that you can't work on items that use X.

B> I checked and no one really uses X.

A> now you use X!

B> I checked and no one really uses Y, I'll use Y for X where X would have really meant X.

..

It doesn't mean null is good, it is simply available. A good meta has entirely different properties than null and those properties get you out of that treadmill.


Null has been called the billion-dollar mistake, so of course Javascript decided to have two of them.


I use null in data because Json.

But otherwise I just leave undefined to play the role of "this optional argument wasn't defined" and null for everything else. I like null because You have to specifically set something as null. I can run into undefined through typos and such. I dont think you can accidentally hit null.

P.s. This reminds me of the one time in Python I thought, "Damn I wish I had both undefined and None". I needed to know if a user was omitting an optional argument or passing an explicit None into it. Ie. This data is optional and the data might be "None". I ended up using kwargs which weakened my function as it nolonger described all the possible arguments.


> I needed to know if a user was omitting an optional argument or passing an explicit None into it.

Sounds like kindof like an Optional<Optional<Value>>. An optional is just a container of at most one element, so you could emulate that with a singleton list, having None mean not specified, [value] be value, and [None] be explicit lack of value.


The solution in Python is to create a sentinel value/object. You can even call it undefined: `undefined = object()`. `None is undefined` ==> false


If you care about whether an optional argument is omitted or not in Python I would default it to false instead of None.


What about boolean optional arguments?


Undefined is “stored” the same way in JSON, which means it isn’t. This only becomes a “problem” when you convert a JSON string to an object and the data contains an array with a null value in it. That’s the only case where null exists in JSON, and I think it’s generally pretty rare.


> Undefined is “stored” the same way in JSON, which means it isn’t.

Null is:

  >> JSON.stringify({"foo": undefined});
  "{}"
  >> JSON.stringify({"foo": null});
  "{\"foo\":null}"


The point is, why use it at all if both values are equivalent to "missing"? I don't see a practical difference between undefined and null in the vast majority of cases, which is the the author is arguing.

Again: In the vast majority of cases. I'm sure you'll find your exceptions.


I've worked with a ton of APIs where these two aren't equivalent.

Consider partial object updates. "null" means "explicitly set a field to null", whereas undefined means "don't change the value of the field". The distinction is meaningful.


In the first case I lost the information that the key exists and can contain a value.

The second form makes it explicit.

On the first case, the parser that's getting the data may want to calculate a sensible value. On the second one, it's clear the value is known.


Not sure I follow. JSON has null. It doesn't have undefined.


You said

> But otherwise I just leave undefined to play the role of "this optional argument wasn't defined"

And the reply is saying that's how JSON uses it too. Anything set to undefined will not be serialized, as if it were optional and not defined.


Arguments belong to functions.

Sorry I was wandering all over.


My personal approach is to pretend the distinction doesn't exist by writing my code as if it doesn't exist. Mostly this means using "foo == null", which works for both. Using undefined as a default convention is well and good, but trying to get to a place where you can guarantee it across a whole project is folly. I've personally seen the JSON case, in particular, result in some fairly pointless extra complexity converting all nulls to undefineds.


Could there be a data structure that allows us to model possibly missing values?

Maybe.


You are right, we might already have an available Option to this.


I think that's a great Result.


There are: 1. falsy values 0, -0, 0n, '', null, undefined, NaN 2. null 3. undefined 4. absence of property and property set to undefined

You have to know differences between those when coding in js/ts/flow. Ie. no 4. has implications when iterating ie. Object.keys({ foo: undefined }).length === 1.

Education around those - yes, but blacklisting null? Sounds a bit silly, however I agree that most developers overuse null when they actually mean undefined.

One example in code where null vs undefined can be used is sql.update(table, where, { foo: null, bar: 1 }) - meaning update foo to null vs sql.update(table, where, { foo: undefined, bar: 1 }) - where undefined means, don't do anything with that column. This helps a lot because it's much more verbose to conditionally construct object with a property or without it, however it's easy to set it defined/undefined.


I feel that the distinction between null and undefined is less significant when you have types such as in TypeScript. The types let you know if a property can be optionally provided and there are constructs that help with that such as the ? operator.


I’ve been trying a new thing lately: never ever use null or undefined or NaN in my own code (external libs you have to account for), instead defining my own invalid types.

So ‘class undefinedDivideByZero extends InvalidType’. Then just check if a value is an instance of InvalidType. Then you can create much more specific invalid types.

If you have to account for 3 anyway, might as well account for N, and then create more information rich types.

Is there a name for this pattern?


Yes. You just reinvented exceptions.


Exceptions are about control flow. Nulls and undefined are about types. The flow generally continues on with the latter. So I’m wondering what the term is for when you have a whole slew of invalid types but continue on without any exception handling and instead handle it via type systems.


In java / C#, this is how their exception works. Indeed it is bad in performance, but you can define exception as flow. For example:

    try{
    }
    catch(SQLException ex) { }
    catch(MathException ex) { }
So it is kinda correct thay you reinvented the exception handling in java / c#, without really raising error / exception itself.


Thanks that's a clear example.

What I'm looking for is a name for a pattern where a function returns (typeof ExpectedType | typeof InvalidType). Say it's a pipelined compiler and you want all the passes to continue even with InvalidTypes, and then at the end of the pipeline the result is a structure of the correct shape, but there may be some "holes" filled with InvalidTypes.

So one way to say it is exception types without the Try/Catch.


> Exceptions are about control flow.

You're thinking of "try ... catch", but that's not what I'm talking about. What I'm talking about, and what I wrote, was "exceptions", i.e., objects that signify you're dealing with an exceptional case.

> Nulls and undefined are about types.

Anything that can be checked with instanceof, as I'm alluding to and as described in the first comment, or cross-checked by comparing a tag against constant, as in the case of node types in the DOM, is "about types".

What you describe as "just check[ing] if a value is an instance of InvalidType" is a straightforward restatement of how to represent an exception and handle it. Give it extra fields with further details, etc. This is widely used, it's just not widely used in in the NodeJS world, but that has more to do with the dubious influence that people like OP have, and all the programmers who try to emulate his style.


I'm only familiar with Exceptions in C++, Java, C#, Python, Javascript, etc. Are there languages where "Exceptions" are used without Try/catch? To me the term implies Exception Types/Try/Catch.


If this isn't clear enough at this point, it's unlikely to be worth anyone's time to explain it (again). An exception can exist and serve a purpose without ever having to be be thrown, and the thing thrown in a throw statement doesn't have to be an exception. Exceptions and "try ... catch" are completely orthogonal. The question is fundamentally... sideways.


Hola GPT3.


Go calls it "errors are values", but readability is poor when every error must be handled many times instead of implicitly returning to callers.


ErrorValues! Thank you! This is exactly what I was looking for. Post: https://blog.golang.org/errors-are-values

> but readability is poor when every error must be handled many times instead of implicitly returning to callers.

I should clarify that I'm only using these types for parsing and transformation pipelines and not as a general replacement for exception handling—in other words only in places where you'd have to already worry about undefineds, nulls, and NaNs.


How are you serialising this value?


Good question!

So far just context dependent. If the target is csv, generally just “”. If JSON, one of the existing types. If DSL, a discrete term from a custom grammar.

But in general so far these tend to be derived intermediate values (maybe when a user has made a partial edit into an invalid state), that usually aren’t serialized.


Default parameters are huge. I'm glad to see this post and hope libraries will adapt. I think pragmatism outweighs the semantic distinction and json serialization concerns. https://github.com/prisma/prisma-client-js/issues/572


This issue was opened on April 1, 2019. Was that a coincidence?


I've been doing the same for some time.

If you use TypeScript and TSLint, there's also a rule for that: https://palantir.github.io/tslint/rules/no-null-keyword


In my job I do a lot of code reviews and I notice that by using Typescript, devs tend to write worse JavaScript code. I often see that developers are assigning `undefined` to properties in order to clean value or to satisfy badly written TS interface, instead of just using JS `delete` statement. Also a lot of times I see passing `undefined` to methods with optional params, instead of just skipping them. Here some bad practices:

If there is an interface: `interface ISomething { x?: string };` and obj of this interface to have `obj.x = undefined` instead of `delete obj.x`.

Writing methods: `function x(param0, param1?, param2?) {}` to be used as `x(1, undefined, '2');` instead of creating the func as `function x(param0, option: { param1?, param2? })` and using as `x(1, { param2: '2' })`.

Returning undefined from a function: `function x(): undefined { return undefined; }`, instead of `function x(): void { return; }`.

Mixing the meanings of `undefined` and `null`, can bring a lot of troubles for JS devs when they use `Object.keys` or using `arguments` in function. IMHO If we keep that `undefined` means missing while `null` means no value, then we will have better JS code, using: `'x' in obj` instead of `obj.x === undefined` or `typeof obj.x === 'undefined'`, `delete obj.x` instead of `obj.x = undefined`, `obj.x = null` and then `obj.x === null` instead of `obj.x == null`.


You should probably never use delete. Not only it mutates objects but also have perf penalty.

> If there is an interface: `interface ISomething { x?: string };` and obj of this interface to have `obj.x = undefined` instead of `delete obj.x`.

In typescript additional properties are not a problem. Since it is using structural typing. In most cases spread and restructuring are better options for merging/overwriting and deleting. It is safer and easier to reason about.

> Writing methods: `function x(param0, param1?, param2?) {}` to be used as `x(1, undefined, '2');` instead of creating the func as `function x(param0, option: { param1?, param2? })` and using as `x(1, { param2: '2' })`.

I am not fan of creating functions taking options objects as argument. In many cases is better to create specialized functions


This kind of thing makes me wish that some variety of Optional/Either/Maybe was a de facto standard in JS, like early promise work turned out.


ReScript (formerly BuckleScript/ReasonML) is a nice option, but I agree in an ideal world these features would exist in the JS standard.


I had this discussion recently in a pull request - the reviewer wanted to know why I was using both null and undefined. Like several people have mentioned, it’s useful in JavaScript in particular to indicate known unknowns as null and unknown unknowns as undefined. Since it was a good question though I actually spent quite a bit of time and energy analyzing this and trying to figure out if my choice still made sense to me.

It’s somewhat reassuring to see these comments here because at the time, I couldn’t deny that my solution isn’t ideal. At the same time, I know that favouring a less explicit approach to describing empty values is inevitably even more confusing - there has to be some way to indicate why something is empty. It’s still hard to program with a solution you know isn’t ideal... I really wanted a better answer or to find a better way forward with the reviewer, but in JS land it seems like an unfortunate but viable way to work.


What's so hard for checking for null or undefined?

All you need to do is use a condition like `v == null` or `v == undefined`. I like the first one more because it's shorter, but both are equivalent to `v === null || v === undefined`

And you can ignore the distinction then.


I try really hard to not complain, because we're stuck with JavaScript.

But I'd just love to tear it down and start again.

For something that's supposed to be a universal programming language, it's got so much stuff like this that makes it so unfriendly to newbies.


>it's got so much stuff like this that makes it so unfriendly to newbies

what's another one?


I work with many developers who don't speak English as a first language and they find things in JavaScript like .filter to be hard to grasp, but .Where in C# makes perfect sense to them because they understand that they're looking for something in a list "Where" their conditions are met.

That's just one example of things in JavaScript that are poorly named for people to guess what they do.


The opposite, actually. "filter" is just a verb you can look up in a dictionary and roughly understand its meaning. "Where" is grammatical and hard to grasp without a holistic understanding of English grammar. Linguistic Moravec paradox, if you will.

Now, explaining Ruby's collect/detect/inject/select/reject, that's a whole different matter...


I don't care what it says in the dictionary, I'm going on real life cases from my colleagues in SEA who don't speak English as a first language.


Scope of var (they fixed this with let)

Automatically creating a variable (with global scope) if you attempt to use a variable that doesn't exist.

Semicolons are optional, except when they aren't.

== and === (at minimum they should be reversed)


>Automatically creating a variable (with global scope) if you attempt to use a variable that doesn't exist.

I just tested this as I think you're describing & it didn't create a global var. Could you give an example?



If you don’t need ie11, wasm works everywhere.

Emscripten can produce js as well as wasm, so you can build both and deliver according to support.


Undefined in js occurs where other languages either throw exceptions or fail compilation. In other words, it represents invalid program state. A language with this "feature" may as well do other insane things, like treat a call to a nonexistent function in some quirky way, like recursively calling the current function. Gotta keep that program running! And if js did that, there would be web devs here on hn arguing that it's a good thing, explaining how to incorporate the feature into designs.

*Edit - recursive rather than no-op, to make it do something that people might abuse.


As others have mentioned, you can’t get rid of null, because of third-party libraries for one. Instead your code should treat them the same at comparison time: ‘== null’ is the canonical way to check for either.


It's kind of awesome that JS has both null and undefined. A lot of languages sometimes wish they had a descriptor for null to indicate if it mean we have the value but it's blank vs we don't have the value. Now, that being said - a JS object can certainly HAVE a value that is undefined - so that's always fun.


I like the semantic differences between null and undefined. I’d rather spend the time cleaning up the codebase to use them appropriately. Maybe you should use typescript and be forced to think about the interfaces of your various functions; their types and how you expect a consumer to use them.


What about external libraries? While it might be a good idea to stop using null in your own code, you should probably still make it a habit to check for both null and undefined to avoid problems with external code.

> we cannot remove null from JavaScript, but we can pretend it doesn't exist

Even if it does become general convention to not use null, as a developer, you cannot depend on that convention and skip null checks in favor of undefined, because null is deeply engrained into the language itself.

So... we can't really pretend that it doesn't exist


The language is uniquely poorly defined for this. Node got this almost right, by baking in a sort of Option type to callback signatures. The community ignored it and ignored failure conditions. It’s bonkers that there’s at least three (yes three, not two; empty array slots are neither null nor undefined, they just resolve to undefined if you access them like any missing property on an object) nullish types in the language but you can’t gloss over that by pretending they don’t exist.


I learnt a lot just reading the thread. I wish the strong mode came to life.


CMIIW, undefined only exists on dynamic typed language. You can't access undefined property in static typed, unless using reflection.

So if anything, you should not set something as undefined, but you can update something as null.


null is negation, undefined is privation, the first is the intent of absence the other the absence of intent




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: