DEV Community

Cover image for Never fail your type checking ever again
Amin
Amin

Posted on

Never fail your type checking ever again

Don't tell me dogs can't smile.

Sometimes, knowing which type we are working with is essential to the success of our script. We could be tempted to use a keyword like typeof. But you shouldn't. Here is why.

"use strict";

console.log(typeof null);
console.log(typeof []);
console.log(typeof {});
console.log(typeof Promise.resolve());
Enter fullscreen mode Exit fullscreen mode

Some of you probably think this is an easy one and that the answer is

null
array
object
promise
Enter fullscreen mode Exit fullscreen mode

And... Nope. Because all these four return the exact same thing.

object
object
object
object
Enter fullscreen mode Exit fullscreen mode

That is because in JavaScript, almost everything is an object. Even the null keyword. Now you read the title and you know there is a better way to check for the type of an object. And you would be right. Introducing: Object.prototype.toString.call.

"use strict";

console.log(Object.prototype.toString.call(null));
console.log(Object.prototype.toString.call([]));
console.log(Object.prototype.toString.call({}));
console.log(Object.prototype.toString.call(Promise.resolve()));
Enter fullscreen mode Exit fullscreen mode

Which will return

[object Null]
[object Array]
[object Object]
[object Promise]
Enter fullscreen mode Exit fullscreen mode

Now you may want to say that yes, this will return the right type, but what's with this ugly formating? We just wanted to have the type, not this garbage string. Also, In the long run, this would be exhausting to type the same thing over and over.

Let's see another example.

"use strict";

function searchNumber(numbers, number) {
    if (Object.prototype.toString.call(numbers) !== "[object Array]") {
        throw new TypeError("First argument must be an array");
    }

    if (Object.prototype.toString.call(number) !== "[object Number]") {
        throw new TypeError("Second argument must be a number");
    }

    const foundNumber = numbers.find(function(currentNumber) {
        return currentNumber === number;
    });

    if (foundNumber) {
        return true;
    }

    return false;
}

console.log(searchNumber([1, 2, 3, 4, 5], 3)); // true
console.log(searchNumber([1, 2, 3, 4, 5], 0)); // false
Enter fullscreen mode Exit fullscreen mode

Here, we defined a function that will help us search a number in an array of numbers. Oh, and by the way, you can use Object.prototype.toString.call on everything. We also did type checking, but it was a lot of characters to type. I think it is time to make it a function.

"use strict";

function type(target) {
    return Object.prototype.toString.call(target);
}

console.log(type(null));
console.log(type([]));
console.log(type({}));
console.log(type(Promise.resolve()));
Enter fullscreen mode Exit fullscreen mode

Which gives us

[object Null]
[object Array]
[object Object]
[object Promise]
Enter fullscreen mode Exit fullscreen mode

Neat! But it would be awesome to mimic what typeof gives us: a simple, plain string with the type of the object. Let's update our function accordingly.

function type(target) {
    const computedType = Object.prototype.toString.call(target);
    const stripped = computedType.replace("[object ", "").replace("]", "");
    const lowercased = stripped.toLowerCase();

    return lowercased;
}
Enter fullscreen mode Exit fullscreen mode

Now we get something cool.

null
array
object
promise
Enter fullscreen mode Exit fullscreen mode

It is time to use it in our function, don't you think?

"use strict";

function type(target) {
    const computedType = Object.prototype.toString.call(target);
    const stripped = computedType.replace("[object ", "").replace("]", "");
    const lowercased = stripped.toLowerCase();

    return lowercased;
}

function searchNumber(numbers, number) {
    if (type(numbers) !== "array") {
        throw new TypeError("First argument must be an array");
    }

    if (type(number) !== "number") {
        throw new TypeError("Second argument must be a number");
    }

    const foundNumber = numbers.find(function(currentNumber) {
        return currentNumber === number;
    });

    if (foundNumber) {
        return true;
    }

    return false;
}

console.log(searchNumber([1, 2, 3, 4, 5], 3)); // true
console.log(searchNumber([1, 2, 3, 4, 5], 0)); // false
console.log(searchNumber({1: "1", 2: "2"}, 2));
// TypeError: First argument must be an array
Enter fullscreen mode Exit fullscreen mode

Now we have a reliable way to compute our types at runtime!

JavaScript also has some isSomething functions that are quite handy if you don't want to use this function and are only using some types like an array which has the Array.isArray method for checking if an object is an array. But I think that using a single function is quite good at normalizing the way you type check in your code.

Thanks for reading. I hope this tutorial has convinced you to type check at runtime and use this little helper function to do this. If you have any questions or notice let me know in the comment section!

Top comments (1)

Collapse
 
anwar_nairi profile image
Anwar

Always wondered if there was a better way, and here it is! Very cool 😉