Skip to content

The odd history of JavaScript’s `null`

turbinelabs October 14, 2020
Reading Time: 4 minutes

Hi, my name is Jev, I’ve been a software developer for 9 months, and these posts are about what I’m learning.

I work at a media, tech, and analytics start-up called Turbine Labs. We deliver thoughtful and unbiased reporting, analytics, and media insights to our customers, all powered by our 3rd generation AI-driven software platform called T3.

Our tech stack is TypeScript/React/Apollo on the Front End — as well as our own homebrewed Styled-Component library dubbed Jetstream — and ElixirElasticSearchPostgreSQLGraphQLNode.js, and Python on the Back End, all running in Kubernetes on GCP.

For an intro to me and my background, just click here, if you’re interested — otherwise, let’s get into it!

Sooooo, how ‘bout that `null`, am I right??? Or, rather, more specifically, sooooo, how ‘bout that `null` in JavaScript, am I right??? Null/nil isn’t anything new in programming languages. The idea of `null` — i.e., a special marker or keyword that basically functions as a stand-in for nothing; a syntactical crutch for an uninitialized, undefined, empty, or meaningless value — has existed since the beginning of computing, and even earlier, of course, in mathematics — thanks to Brahmagupta giving `null`/`nil`/`zero`/`0` it’s first computing rules in the middle of the 7th Century.

In fact, almost every programming language I’ve read about or used — and to be sure that list is NOT long — needs something to handle an intentional absence of data (even languages like Haskell has `Maybe` and Guava has `Optional`).

With that said, JavaScript is no exception: `null` is one of the (now) 7 JavaScript “primitive types”, along with `string`, `number`, `bigint`, `boolean`, `symbol`, and `undefined` (or IS it? More on that in a bit).

In a current project on which I am working, we needed an easy way to represent a ternary set of values that would be returned to me from our API. We could, of course, type out an enum in Absinthe GraphQL library to represent the three values, say,

enum :client_status do 
  value(:all_clients)
  value(:only_current_clients)
  value(:only_ex_clients)
end

But writing a specific enum for this simple triple value seemed overkill, so instead, we decided to just use a `boolean`. I know what you’re thinking, a `boolean` expression can only have one of two values: `true`, or `false` — but we need three. This is correct, but fortunately, `nil` in Elixir, and `null` in JavaScript are also readable/usable values. So, for our `:all_clients` value, I would receive a `nil` from our API (which JavaScript read as `null` after our GraphQL layer sends it to us), for our `:only_current_clients` value, I would receive and `true`, and for `:only_ex_clients`, a `false`.

Now, `null` is a “falsey” value in JavaScript (just like `undefined`, `false`, `0`, `“”` (an empty string), etc.), so I needed to make sure I did a proper type-check on the value when it was returned to me — since both `false` and `null` would be ignored exactly the same in expressions like the following ->

if (nullishValue) {
  // This will not be logged, since a `null` value is “falsey” console.log(“will be ignored”);
} else {
  // This WILL be logged console.log(“this WILL happen”);
};if (falseValue) {
  // This will not be logged, since `false` value is “falsey” console.log(“will also be ignored”);
} else {
  // This WILL be logged console.log(“again, this WILL happen”);
};

“No problem!” I figured, “I’ll just use the JavaScript `typeof` operator to do my check, and we’ll be good to go.”

// Here's our ternary// This is the value the API returns returnedValue// If the value is “truthy”, i.e., `true`, we set component state to “only_current_clients” ? setClientState(“onlyCurrentClients”)// Here’s where we need to distinguish between the 2 “falsey” values: `null` and `false` : typeof returnedValue === “null”// If typeof === “null”, set component state to “only_ex_clients” ? setClientState(“onlyExClients”)// Else set component state to “all_clients”, because the only other value is `false` : setClientState(“allClients”)

EZPZ, right? Wrong. In fact, this broke on me as soon as I wrote it. Here’s why: there is no `typeof === “null”` in JavaScript. Check out the following:

typeof “hello world” // “string”typeof 100 // “number” // No distinction between `floats` and `integers` in JS: everything’s just a “number” typeof 0.00001; // “number” typeof Infinity; // “number” // Not a Number? A “number”. C’mon. typeof NaN // “number”typeof true // “boolean” typeof false // “boolean”typeof 1n // “bigint”let sym = Symbol(‘foo’);
typeof sym // “symbol”const foo = () => {};
typeof foo // “function”typeof undefined // “undefined”typeof []; // “object” typeof [1, 2, 3, “stringValue”]; // “object” typeof {}; // “object” typeof { firstKey: “firstValue”, secondKey: “secondValue” }; // “object”

Perfect! This is all well and good, and pretty much what we expected (other than Not a Number (NaN) being Number type *eye roll*) but what about `null`?

typeof null // “object”

Huh?

This is where things broke for me. I assumed — you know what they say about assuming — that null, since it’s primitive, and all other primitives can be type-checked vis-a-vis the `typeof` operator, that `null` was the same.

Wrong, chump.

In JavaScript, `null` is marked as one of the “primitive” values, because its behavior is — and this is the key: seemingly primitive. Yep; seemingly primitive.

According to the MDN docs:

“In the first implementation of JavaScript, JavaScript values were represented as a type tag and a value. The type tag for objects was 0. `null` was represented as the NULL pointer (0x00 in most platforms). Consequently, `null` had 0 as type tag, hence the “object” typeof return value. A fix was proposed for ECMAScript (via an opt-in), but was rejected. It would have resulted in `typeof null === ‘null’`.”

For a more detailed description of the above, check out this post by Dr. Axel Rauschmayer, in which the good doctor actually looks at the C code for the `typeof` operator and explains more in-depth how type-tags and values were actually stored in the C code.

But, there ya’ have it. (`typeof null`) returns “object” for the same reason most other silly code-bugs exist: we really just didn’t think far enough ahead. In fact, the `typeof null === ‘null’` was rejected NOT because it’s a bad idea, or illogical, or would have unintended behavior; instead, it was rejected simply because there was already too much code written that would break if it was changed. That simple.

Our final solution interestingly would have worked in the first place, and is also incredibly close to what we attempted the first time:

returnedValue ? setClientState(“onlyCurrentClients”)
  // This time simply remove the `typeof` operator... : returnedValue === “null” ? setClientState(“onlyExClients”) : setClientState(“allClients”)

This is my takeaway from this learning experience: all code, in every language, has behavioral eccentricities that are simply unpredictable without experience or knowledge. You WILL be caught off guard from time to time when your code doesn’t act as predicted — EVEN if your logic and execution are theoretically flawless. But when stuff breaks for unforeseen reasons — and it WILL! — take a small amount of time to do your research into why it broke. So much of how JavaScript works behind the scenes is completely abstracted away (which isn’t bad. In fact, it’s one of the reasons JavaScript is so friendly to new developers). Pull back the curtain a bit. See the JavaScript stage as more than just a sum of its parts.