DEV Community

Cover image for The weird JavaScript type system - typeof and NaNs
Ahmed Osama
Ahmed Osama

Posted on

The weird JavaScript type system - typeof and NaNs

Heya, nice to see you again in another article, today's topic is kind off functional programming, I thought it my be nice to get back the basics and talk about types which is one of my favourite topics.

So why not have a series that's dedicated to the typesystem that JS has?

In this article I'll to go all beasty about types and why you should really care about types, so hold tight and let's go.

"Stop using double equal, tripe equal for the way", you often see this recommendation in some courses, blogs, and even books that favors the tripe equal === over the double equal == as they claim it's better in terms of handling corner cases. HELL NO

I won't argue that back in time I believed such claim and even spread it in some of my courses, articles. But meh, we all do silly things.

So, let's get right into our topic and understand why I am so that irritated of this claim

What types does JS have?

Surely you know what types the language you use supports, but bare with me and let's revisit them swiftly.

  • Primitives

    • String
    • Number
    • Undefined, NULL
    • Symbol
    • Boolean
  • Object

    • object (The object notation or an instance of a class maybe)
    • Array
    • Function

All the primitive types are treated by javascript as non-reference values a.k.a they get stored on the stack not the heap (We'll have another article about that I promise).

And all the descendendants of the Object type are treated as Referenceable values, as they get stored on the heap and their reference is stored on the stack

The typeof Operator

This operator is kinda special as it's the only operator in javascript that can access non-existing variables in any scope, let's see the following example.

Undeclared

console.log(typeof undeclared) // undefined
Enter fullscreen mode Exit fullscreen mode

Apparently, the variable undeclared doesn't exist in any scope, yet the typeof can access it, but what distrubs me that it returns undefined :/

Well, it might seem reasonable, but IMHO, I like to think of undefined as a representation of no value, more like the absence of a value, and I might assume that you too think of it like that, more like a placeholder to an upcoming value, consider the following example.

class User {
  protected db: Database

  constructor(db: Database) {
    this.db = db
  }
}
Enter fullscreen mode Exit fullscreen mode

You're expecting the variable db to be instance of some Database entity, maybe you're injecting it to your User model to handle incoming requests related to database persistency.

Apart from that, what is the value of db before you pass it any instance? Yup, it's undefined, you haven't setted a value for it yet, so the absence of a value is represented by the keyword undefined, hopefully that makes sense.

But if so, what's the problem with returning undefined using typeof operator for non-existing variable?

Well, I agree with Kyle Simpson that this is some sort of confusion, using the same data type for two separate concepts is not accurate, JavaScript could've introduced new type called undeclared to indicate that some variable is not defined in any accessible scope.

Further, neither me nor Kyle thought of this mental models by ourselves nor did anyone has this mental model, actually the EcmaScript guidelines says so.

EcmaScript description of the undefined type

But meh, adding something like this may cause a lot of bugs to existing code basis, you never know if someone uses such code.

Null is an object

let v = null

console.log(typeof v) // object
Enter fullscreen mode Exit fullscreen mode

One of the weirdest parts of JavasScript, that it treats the null as if it's an object.

Well, I think there are two mental models that justify this behavior.

  • Null is a representation of an empty object
  • Null is just equal to undefined and that's a JS bug

Well let's walk through each behaviour and I'll let you decide which one makes more sense.

Null is an empty object

Consider the following piece of code.

let nullObject = Object.create(null)

console.log(nullObject)
Enter fullscreen mode Exit fullscreen mode

The output of this code would be something like [Object: null prototype] {}, thus there are some opinions or thoughts that the null type in JS is treated as object because it can be used as a prototype to other objects a.k.a if you want to create functionless -if you will- object which has no prototypes, therefore, it has any built-in support from JS object functionalities.

let nullObject = Object.create(null)
let anotherObject = {}

console.log(anotherObject.toString()) // [Object object]
console.log(nullObject.toString()) // Throws an error
Enter fullscreen mode Exit fullscreen mode

And once again, I am not making this up, that's what the EcmaScript specs says about the null type

ES specs description about null value and type

Null is undefined

I think this way of thinking is based on the idea that console.log(null == undefined) returns true or maybe you're like me, shifting from another language (PHP in my case) to JavaScript, either way, I think those both mental models are incorrect in some sense.

The first one totally ignores the fact that console.log(null === undefined) returns false which makes them completely different, and the second one judges JavaScript by other languages'rules, which is more worse.

If you're doing JavaScript, do it in JavaScript way - Panda's Law #2

Yet, there are some corner cases that I'd like to treat null and undefined as equal types: If it makes sense!!

If I can write more concise code using the fact that they're equal in abstract (We'll explain this later in the Equality section of this series), sure I'll do so.

Arrays are objects

This one is quite easy I'd say, but let's walk through it.

let ages = [20, 21, 26, 42]

console.log(typeof ages) // "object"
Enter fullscreen mode Exit fullscreen mode

This one is completely reasonable, as I mentioned earlier, the V8 engine treats arrays and objects alike in some ways to reduce the duplications in memory allocation (I promise there will an article for that..)

Therefore, if you want to make sure that some variable is an array, you have to use the Array constructor

let ages = [20, 21, 26, 42]

console.log(Array.isArray(ages)) // true
Enter fullscreen mode Exit fullscreen mode

And I think the rest of the types are quite reasonable about, like typeof function(){} returns "function" and so on.

The special type NaN

The #1 misunderstood type in JS, I think if IEEE was paid 1 dollar for everytime the NaN thought to be not a number they would've been #1 in Nasdaq..

Yeh the NaN is made to be though of as Not a number but meh, not the real meaning of not being a number!!

console.log(typeof NaN) // number
Enter fullscreen mode Exit fullscreen mode

Like c'mon! the typeof operator says it's a number :"

I'd blame IEEE for not calling it NavN which stands for Not a valid number or NarN which stands for Not a representable number because that's what NaNs are.

consider the following examples.

console.log(Number('abc')) // NaN
console.log(Number({ x: 1, y: 2 })) // NaN
console.log(Number([1, 2, 3, 4])) // NaN
console.log(Number('5')) // 5
console.log(Number(true)) // 1
Enter fullscreen mode Exit fullscreen mode

So NaNs actually are numbers, but special number that cannot be represented in a numeric form, and I'll refer back to why we're having this argument.

Now that's done with, I'll see you in the next part ^^

Have a nice drink and a wish you a very pleasing day, Cheerio 💜

Farewell

Consider supporting/following me

Top comments (0)