Hacker News new | ask | show | jobs
by jeswin 1711 days ago
Here's another. Instead of returning Sometype|undefined from a function which may or may not have a value to return (such as searchCustomer), return Sometype|null.

That forces the function to return a value that's explicitly intended rather than defaulting from a missed out if-else codepath. This is useful since JS is often imperative style code.

6 comments

The difference between null and undefined in JavaScript is something I wished had never been implemented. Other languages refer to null as their billion dollar mistake, but somehow JavaScript got 2 of them with slightly different but sometime identical behaviour. I would defer to eslint to prevent this particular issue if you care about it, this allows you to set rules in your own code without any impact to the outside world.

I have only seen null vs undefined lead to 2 things in my experience: mistakes and bikeshedding.

> Other languages refer to null as their billion dollar mistake

The "billon dollar mistake" as described by Tony Hoare was not nulls per se.

The billion dollar mistake was having a type system where null was a member of every reference type. This does not apply to language like JavaScript without static type checking, and it doesn't apply to type systems like TypeScript where null or undefined have to be explicitly specified as members of a type.

The undefined/null distinction solves an additional problem: In Java you don't know if a value is null because a field wasn't initialized correctly or because it was deliberately set to null. JavaScript allows you to distinguish between these two scenarios.

exactly.. the 'only' billion dollar mistake in JS in regards to null is that typeof null === 'object' => true
It’s not undefined, therefore it’s an object. And because every type can be null, it makes sense that it’s just “object”, not “string” or whatever.
It it just a mistake or oversight in the definition of the language. It doesn't really make logical sense that a null is an object but a string is not an object.

It would kind of make sense in Java, where only object types can be null. But that distinction does not exist in JavaScript.

typeof null should be "null", just how typeof undefined is "undefined"

it is an implementation bug of netscape, null was represented by the zero pointer and objects where tagged pointer with a tag of zero, so when reading its tag null looked like an object.

there are solutions as x == null, x === null, and Object(x) == x allow you to check for null|undefined, null, and object values, but typeof null being "object" is purely a specification bug that it is too late to change.

I've always liked the two-nulls solution in JS. `undefined` is a runtime-generated missing value, whereas `null` is a compile-time author-supplied missing value. In other words `undefined` is a "pulled" missing value, `null` a "pushed" missing value. Any feature can be misused, but having the distinction is certainly helpful.
I'm fond of this distinction as well. One could also parse it semantically as `undefined` meaning "unknown unknown" vs `null` being a "known unknown" (or "this value left intentionally blank").

Where I think it falls down in practice, is that JS still treats undefined as a legitimate pseudo-value, as opposed to a read-only return result for a missing key. So for instance, `x=[0,1]` and `x=[0,1,undefined]` will both return undefined for `x[2]`, and it takes jumping through some hoops to know if that value was undefined on purpose, or if the key is simply not found.

If I had my druthers, attempting to set a value as undefined would either throw a fatal error, or be an alternate syntax to unset a value (such that `x.length` would equal 2 in both examples above).

I agree. I can understand the parent but I like these distinctions and Typescript makes them easier to deal with for me
When people refer to the "billion dollar mistake", they mean the language feature that every value could potentially be `null`, i.e. even if a function returns MyType, it could also return `null`.

TypeScript in strict mode (the default) still has `null` and `undefined`, but not the billion dollar mistake: if you want to be able to pass `null` to a function, you have to mark that parameter as being potentially `null`.

> I have only seen null vs undefined lead to 2 things in my experience: mistakes and bikeshedding.

I disagree, though I think the implementation leaves something to be desired. Primarily, I think there is fundamentally a difference between the value of obj.bar in the following examples that is useful to differentiate between:

{ foo: 'hello' }

{ foo: 'hello', bar: null }

For example, GraphQL makes specific use of this when dealing with input types for mutations: null essentially means "delete this field" while unset means "don't change it".

There is a very good discussion on this topic here, https://github.com/graphql/graphql-js/issues/133 , which goes into the rationale behind it, how it's supported in languages that do NOT differentiate between null and undefined, and how some folks changed their minds on the issue.

Both Flow and CoffeeScript got this right, while TS is slowly dragging its feet towards the right solution: Pretend there’s no difference between them. Your code will be easier to reason about. If you need two different “other values”, use a proper enum / type union / restructure your API.
I disagree. `null` in TypeScript is equivalent to `None` in many other typed languages. `undefined` in Typescript is like null in other languages, with the caveat that if you’re working to transition an untyped codebase and trying to bring types, there may be a useful place for `undefined` in order to express that there is a lack of safety / strict-handling in that area.

I’m still not sure about Error handling, though. Seems feasible that in a fully typed project, any possible unhandled error type could raise a compile error. AFAIK there’s nothing (beyond catch + exhaustive switch) to handle exhaustive error checking in TypeScript, nor is there lib support for handling it either.

Which language has both a "None" and "null"?
Scala, as I recall. It encourages using Options (Some/None), but since it runs on the JVM and will often interop with Java libraries, you can also have nulls.

Not exactly a language design, but an unfortunate reality.

Oh no! Does it not have a type for NonNull references, like Rust does?
Since Scala 3, it can have total static analysis where nulls become a separate type that has to be explicitly declared in type signatures.

So, yes.

javascript, at least, has "undefined" and "null", an infuriating duality of falsiness. PHP also has a notion of not being set as well as being set but null.
JavaScript is what we are talking about in this thread, making the point that JavaScript is similar to JavaScript does not help ;-)
+1, and this practice can be verified by TS with the `noImplicitReturns` setting (https://www.typescriptlang.org/tsconfig#noImplicitReturns)
Yeah, that rule is super powerful!
No need to use null for this, undefined works equally well with noImplicitReturns.

For example: https://www.typescriptlang.org/play?#code/LAKAZgrgdgxgLgSwPZ...

I prefer to use undefined over null since it just fits more naturally with other TS features like optional fields. But I agree with using tsc’s no implicit returns check.
The use of null in place of undefined is not composable with optional parameter behaviour, among other things.
You can declare Sometype|void return type. So the compiler will check that you either don't use the return type or treat it as Sometype. Of course this depends on the logic and for failed routes you should return appropriate results.
void implies that the return type should is undefined behavior and should not be relied upon, so that something like this

    const x = foo();
is incorrect when foo() returns void. 99% of the time x will be undefined (the value) but there are cases where it would not be. For example

    arr.forEach(x => x.sort())
sort returns a value as well as having a side effect. But forEach expects a void callback. This code is perfectly fine in JavaScript because forEach does not read the return value of the callback.
I agree. You should type something as null if you need to force callers to deal with the null value and can't do that with an exception.

Type it as void if the value isn't really important to the caller, or you'll throw exceptions in an exceptional case.

Common wisdom is to always have user-defined functions return void, but sometimes I think it's okay to use void if you're replacing a built in JavaScript functionality so the outer code was relying on that semantic. For example, replacing a simple usage of findIndex (that returned undefined) with something more complex that does API calls.

I agree. I don't see where what you wrote contradicts what I said.
Perhaps they were not trying to contradict you?