There is a BUG in JavaScript Since Day One — ‘typeof null’

And it seems that there will always be.

Omer Aslan
JavaScript in Plain English

--

As you know, all values in JavaScript are either primitives or objects.

There are 7 primitive data types:

-string

-number

-bigint

-boolean

-undefined

-symbol

-null

Let’s check their types with typeof operator:

console.log(typeof "some text");   // stringconsole.log(typeof 1234);    // numberconsole.log(typeof 9007199254740991n);  // bigintconsole.log(typeof true);   // booleanconsole.log(typeof undefined);    // undefinedconsole.log(typeof Symbol('some text'));  // symbolconsole.log(typeof null);    // object

Wait! What?

Why is typeof null an object?

This is a bug that can’t be solved since it would cause the current code to break.

Let's dive deep.

What is the difference between objects and primitives:

Primitives are immutable, you can’t add properties to them:

let myString = "abc";// try to add property "figure" myString.figure = 4; console.log(myString.figure) // undefined 

And primitives are compared by value, they are considered equal if they have the same content:

console.log("abc" === "abc") // true

Objects. All non-primitive values are objects. Objects are mutable:

let myObject = {};// try to add property "figure"myObject.figure = 123;  

console.log(myObject.figure); // 123

And objects are compared by reference. Each object has its own identity and two objects are only considered equal if they are, in fact, the same object:

console.log({} === {})    // false

let myObject = {};
console.log(myObject === myObject) // true

Wrapper object types. The primitive types boolean, number and string have the corresponding wrapper object types Boolean, Number, and String. Instances of the latter are objects and different from the primitives that they are wrapping:

console.log(typeof new String("abc") )    // objectconsole.log(typeof abc)    // string

console.log(new String("abc") === "abc") // false

typeof is an operator that categorizes primitives and helps distinguish them from objects

The “typeof null” problem is a holdover from JavaScript’s original version.

Values were kept in 32 bit units in this version, which included a short type tag (1–3 bits) and the actual data of the value. The type tags were saved in the units’ lower bits.

type tags(1–3 bits)+ value (29–31 bits)= total 32 bits

There were five type tags:

  • 000: object. The data is a reference to an object.
  • 1: int. The data is a 31 bit signed integer.
  • 010: double. The data is a reference to a double floating-point number.
  • 100: string. The data is a reference to a string.
  • 110: boolean. The data is a boolean.

There are 2 special values:

  • undefined (JSVAL_VOID) was the integer −2³⁰ (a number outside the integer range).
  • null (JSVAL_NULL) was the machine code NULL pointer. Or: an object type tag plus a reference that is zero.

Because it starts with 000 (which is object type tag), the type of thinks it is an object.

The engine code for typeof:

JS_PUBLIC_API(JSType)
JS_TypeOfValue(JSContext *cx, jsval v)
{
JSType type = JSTYPE_VOID;
JSObject *obj;
JSObjectOps *ops;
JSClass *clasp;

CHECK_REQUEST(cx);
if (JSVAL_IS_VOID(v)) { // (1)
type = JSTYPE_VOID;
} else if (JSVAL_IS_OBJECT(v)) { // (2)
obj = JSVAL_TO_OBJECT(v);
if (obj &&
(ops = obj->map->ops,
ops == &js_ObjectOps
? (clasp = OBJ_GET_CLASS(cx, obj),
clasp->call || clasp == &js_FunctionClass) // (3,4)
: ops->call != 0)) { // (3)
type = JSTYPE_FUNCTION;
} else {
type = JSTYPE_OBJECT;
}
} else if (JSVAL_IS_NUMBER(v)) {
type = JSTYPE_NUMBER;
} else if (JSVAL_IS_STRING(v)) {
type = JSTYPE_STRING;
} else if (JSVAL_IS_BOOLEAN(v)) {
type = JSTYPE_BOOLEAN;
}
return type;
}

It starts with checking if the value v is undefined (VOID) in the statement:

if (JSVAL_IS_VOID(v))

Then it checks if the value is object. Checking if there is a type tag starting with 000.

If it is not an object it checks if it is a number, string, or boolean.

There is not any check for NULL.

It is a bug that can be seen easily, but since it was not fixed in time, it became much more difficult to fix afterward.

Even the creator of JavaScript, Brendan Eich commented on a blog about this subject stating that this is a bug indeed.

Brendan Eich once considered the need to fix typeof , but rejected that proposal stating: “I think it is too late to fix typeof. The change proposed for typeof null will break existing code.”

In another discussion, he wrote: “In general, typeof seems like a mess that will be hard to reform sensibly. We have reason to believe typeof null === “object” *is a bug* that could bite real content, from our spidering of the web. It might be best to leave typeof utterly alone and deprecate it, *but I’m still in favor of the null bug-fix*.”

More content at plainenglish.io. Sign up for our free weekly newsletter. Get exclusive access to writing opportunities and advice in our community Discord.

--

--