Check if a value is an object in JavaScript
The snippet:
function isObject(maybeObject) {
return Object.prototype.toString.call(maybeObject) === "[object Object]"
}
Problem
Let’s say you want to find out if a value is an object in JavaScript.
I used to do something like this:
function isObject(value) {
if (typeof value === "object") {
return true
}
return false
}
But here comes null
and []
to cause trouble:
typeof null === "object" // -> true
typeof [] === "object" // -> true
Alright, let’s deal with those. Maybe we could get away with something like this:
function isObject(maybeObject) {
if (typeof maybeObject === "object" && value !== null && !Array.isArray(maybeObject)) {
return true
}
return false
}
But then more troublemakers appear:
typeof new Date() === "object" // -> true
typeof Math === "object" // -> true
typeof JSON === "object" // -> true
A solution
function isObject(maybeObject) {
return Object.prototype.toString.call(maybeObject) === "[object Object]"
}
Using Object.prototype.toString
in this way comes quite in handy here since it allows us to use the toString()
like a utility function.
How does it look when we don’t pass an object?
Object.prototype.toString.call('hello world') // -> "[object String]"
Object.prototype.toString.call(2) // -> "[object Number]"
Object.prototype.toString.call([]) // -> "[object Array]"
Object.prototype.toString.call(null) // -> "[object Null]"
Object.prototype.toString.call(JSON) // -> "[object JSON]"
A little extra for TypeScript
For TypeScript we could add some extra niceties:
function isObject(maybeObject: unknown): maybeObject is Record<string, any> {
return Object.prototype.toString.call(maybeObject) === "[object Object]"
}
For type safety we could declare that maybeObject
is of type unknown
. We don’t know what the caller will pass in.
The second part; maybeObject is Record<string, any>
is a type predicate to say what the type should be if the function returns true
. Without it the thing we pass in will retain its type - no bueno:
// isObject WITHOUT maybeObject is Record<string, any>
function someOperation(data: unknown) {
isObject(data) {
data // <- Type is unknown. We would have to narrow it down here to work with it.
}
}
// isObject WITH `maybeObject is Record<string, any>`
function someOperation(data: unknown) {
isObject(data) {
data // <- Type is Record<string, any> within the closure.
}
}