DEV Community

loading...

Javascript types and type checking

mikkel250
I've been tinkering with computers since I was a teen. I'm currently pivoting from my current role as Tech Support manager to Full Stack Web Developer. I'm actively seeking employment in the field.
・4 min read

Types

Note: throughout this post, I shorten JavaScript to 'JS'.

The reason for the idea that "everything is an object" in JS is because most of the values in JS can behave as objects. But this doesn't make them objects.

JS Types that differ from objects are Primitive Types:

  • undefined
  • null
  • string (the string literal, created "with quotes like this")
  • number
  • boolean (these are actual special values true and false, not 0 and 1 like some other languages)
  • symbol (added with ES6)
  • bigint (added in ES10, 2019)

Other things that behave like types that are not explicitly listed as types in the spec:

  • undeclared
  • null (quirky, due to a historical bug)
  • function (referred to as a subtype of the object type, a "callable object")
  • array (a subtype of the object type, even though it has specific behavior)

The only items in JS that are actual objects are:

  • object
  • function
  • array

Variables, types, and typeof

Unlike in other languages where the type is declared when creating the variable, such as in C, int myNum = 3;, In JS, variables don't have types, but the values contained in the variable do, so this is how they are evaluated, and can change over time depending on what they are assigned to.

var v;
typeof v;   // "undefined"

v = "1";
typeof v;   // "string"

v = 2;
typeof v;   // "number"

v = true;
typeof v;   // "boolean"

v = {};
typeof v;   // "object"

v = Symbol;
typeof v;   // "symbol"

The typeof operator will always return a string, and there is a short list of values it can be returned (essentially an enum list). So, when checking for type, always make sure to put quotes around the value you are checking for, or the expression will not evaluate as expected.

let v = undefined;

typeof v === "undefined";  // true

typeof v === undefined;  // will return false

Note that there is some "quirky" behavior when using typeof.

typeof doesntExist;   // "undefined"

var v = null;   // "object"

v = function() {}; 
typeof v;   // "function"

v = [1, 2, 3];
typeof v;   // "object"

v = 42n;
// or: BigInt(42)
typeof v;   // "bigint"

Note that null will return the "object" type, so it can fail checks. This can basically be treated as a bug in the language (but there are historical reasons for it). For this reason, if you want to assign a variable to an "empty" value, it's best to avoid using null. Either leave it blank or assign it to undefined if you want to be more explicit. The same goes for assigning 0 as a 'placeholder' value to a variable, since zero is a number, and the variable will evaluate to that type and can cause unplanned-for behavior.

Note also that when checking a variable that didn't exist (above), it still returned "undefined". There is no "undeclared" type in JS, so you could get undefined back from a check even if the variable has never been declared, but you can also get "undefined" back when the variable has been declared but not yet assigned a value. The typeof operator is the only operator in JS that is able to reference a thing that does not exist and not throw an error.

if (typeof DoesntExist) {
  console.log("passed the if check");
}
// evaluates to true, logs "passed the if check"

if (DoesntExist) { 
  console.log("passed");
} 
// this will throw an error

// even more explicit
if (typeof DoesntExist !== "undefined") {
  console.log("this will only return if the variable exists and something has been assigned to it");
}

The full list of what typeof can return is:

Type Result
Undefined "undefined"
Null "object"
Boolean "boolean"
Number "number"
BigInt (new in ECMAScript 2020) "bigint"
String "string"
Symbol (new in ECMAScript 2015) "symbol"
Function object (implements [[Call]] in ECMA-262 terms) "function"
Any other object "object"

Special Values: NaN

A good mental model to use for NaN is "invalid number" as opposed to the instinctual "Not a Number".
NaN is returned if, for example, you try to convert an invalid string into a number, or if JS tries to do so through implicit conversion.


var x = Number("n/a");  // NaN
var y = Number("39") // 39  here JS does implicit conversion
y - x; // NaN   because JS implicitly tries to convert x to a number

x === x  // false 

NaN is the only value in JS that is not equal to itself, which is how the x === x comparison can fail.

JS does ship with a way to check for the NaN value, isNaN(), but this too can have some quirks. The reason is because it first tries to coerce the value passed in to a number, which can result in false positives.

isNaN("a string");   // true

Above, it is coercing the string to a number, which results in the NaN value, so it returns true (the same way declaring x in the block above it did).
As a result of this unexpected behavior, ES6 shipped with a new utility Number.isNaN(), which will not try to coerce the parameter to a number first.

Number.isNaN("a string");   // false

Special Values: Negative zero -0

The negative zero can have some unexpected consequences. When using the -0, be aware of some of the gotchas when using comparison operators, demonstrated below.

let trendRate = -0;
trendRate === -0;  // true

trendRate === 0;  // true 
trendRate < 0;   // false
trendRate > 0;   // false

// since the triple equals doesn't reliably return comparisons to zero, 
// the Object.is() method was introduced, which can reliably check for -0
Object.is(trendRate, -0);  // true
Object.is(trendRate, 0);  // false

trendRate.toString();  // "0"  the sign is dropped when converted to a string

The takeaway from the above is that, if you need to check for a negative zero, use the Object.is() method, and if you need it in string form, don't use the toString() method. One option would be to create your own function that will check the parameter using the Object.is() method, and then create and return a string literal "-0".

function negZeroToString(input) { 
  if (Object.is(input, -0)) {
    return "-0";
  } else {
    return new Error (`${input} is not the number negative zero! This function only accepts negative zero as an argument.`);
  }
}

And that's a brief overview. Hopefully you like this 'type of' post (womp womp)!

Discussion (0)