171

The typeof operator doesn't really help us to find the real type of an object.

I've already seen the following code :

Object.prototype.toString.apply(t)  

Question:

Is it the most accurate way of checking the object's type?

Royi Namir
  • 144,742
  • 138
  • 468
  • 792

9 Answers9

226

The JavaScript specification gives exactly one proper way to determine the class of an object:

Object.prototype.toString.call(t);

http://bonsaiden.github.io/JavaScript-Garden/#types

D M
  • 5,769
  • 4
  • 12
  • 27
kmatheny
  • 4,042
  • 1
  • 19
  • 12
  • 6
    If you're looking for a specific type you would likely want to do something along the lines of: `Object.prototype.toString.call(new FormData()) === "[object FormData]"` which would be true. You can also use `slice(8, -1)` to return `FormData` instead of `[object FormData]` – Chris Marisic Dec 17 '14 at 00:36
  • 4
    Is there any difference between using `Object.prototype` and `{}`? – GetFree Jun 13 '15 at 05:25
  • 4
    maybe this has changed over the years, but `Object.prototype.toString.call(new MyCustomObject())` returns `[object Object]` whereas `new MyCustomObject() instanceOf MyCustomObject returns true` which is what I wanted (Chrome 54.0.2840.99 m) – Maslow Nov 18 '16 at 18:54
  • @Maslow, I've ran into the same issue that you brought up. After looking through some documentation online, I ended up using `new MyCustomObject().constructor === MyCustomObject`. – solstice333 Apr 15 '17 at 22:23
  • 5
    It begs the question, why have they no wrapped this code in a more convenient method, or allowed an additional operator compile to this. I know you are only the messenger, but quite frankly that is awful. – Andrew S Apr 01 '18 at 16:04
  • "Exactly one way"? Hogwash. `instanceof`. – Heretic Monkey Jul 13 '22 at 16:19
66

the Object.prototype.toString is a good way, but its performance is the worst.

http://jsperf.com/check-js-type

check js type performance

Use typeof to solve some basic problem(String, Number, Boolean...) and use Object.prototype.toString to solve something complex(like Array, Date, RegExp).

and this is my solution:

var type = (function(global) {
    var cache = {};
    return function(obj) {
        var key;
        return obj === null ? 'null' // null
            : obj === global ? 'global' // window in browser or global in nodejs
            : (key = typeof obj) !== 'object' ? key // basic: string, boolean, number, undefined, function
            : obj.nodeType ? 'object' // DOM element
            : cache[key = ({}).toString.call(obj)] // cached. date, regexp, error, object, array, math
            || (cache[key] = key.slice(8, -1).toLowerCase()); // get XXXX from [object XXXX], and cache it
    };
}(this));

use as:

type(function(){}); // -> "function"
type([1, 2, 3]); // -> "array"
type(new Date()); // -> "date"
type({}); // -> "object"
George Stocker
  • 57,289
  • 29
  • 176
  • 237
wiky
  • 6,178
  • 3
  • 16
  • 10
  • That test on jsPerf isn't quite accurate. Those tests are not equal (testing for the same thing). E.g., typeof [] returns "object", typeof {} also returns "object", even though one is an object Array and the other is an object Object. There are many other problems with that test... Be careful when looking at jsPerf that the tests are comparing Apples to Apples. – kmatheny Sep 26 '12 at 19:43
  • Your `type` function is good, but look at how it does compared to some other `type` functions. [http://jsperf.com/code-type-test-a-test](http://jsperf.com/code-type-test-a-test) – Progo Mar 20 '14 at 12:26
  • 26
    These performance metrics should be tempered with some common sense. Sure, the prototype.toString is slower than the others by an order of magnitude, but in the grand scheme of things it takes on average a couple hundred *nanoseconds* per call. Unless this call is being used in a critical path that's very frequently executed, this is probably harmless. I'd rather have straight forward code than code that finishes one microsecond faster. – David Oct 09 '14 at 14:58
  • `({}).toString.call(obj)` is slower than `Object.prototype.toString` http://jsperf.com/object-check-test77 – timaschew Oct 10 '15 at 10:41
  • Nice solution. I borrow your function into my lib :) – Dong Nguyen Apr 08 '16 at 08:01
23

Accepted answer is correct, but I like to define this little utility in most projects I build.

var types = {
   'get': function(prop) {
      return Object.prototype.toString.call(prop);
   },
   'null': '[object Null]',
   'object': '[object Object]',
   'array': '[object Array]',
   'string': '[object String]',
   'boolean': '[object Boolean]',
   'number': '[object Number]',
   'date': '[object Date]',
}

Used like this:

if(types.get(prop) == types.number) {

}

If you're using angular you can even have it cleanly injected:

angular.constant('types', types);
orad
  • 15,272
  • 23
  • 77
  • 113
parliament
  • 21,544
  • 38
  • 148
  • 238
12
var o = ...
var proto =  Object.getPrototypeOf(o);
proto === SomeThing;

Keep a handle on the prototype you expect the object to have, then compare against it.

for example

var o = "someString";
var proto =  Object.getPrototypeOf(o);
proto === String.prototype; // true
Raynos
  • 166,823
  • 56
  • 351
  • 396
6

I'd argue that most of the solutions shown here suffer from being over-engineerd. Probably the most simple way to check if a value is of type [object Object] is to check against the .constructor property of it:

function isObject (a) { return a != null && a.constructor === Object; }

or even shorter with arrow-functions:

const isObject = a => a != null && a.constructor === Object;

The a != null part is necessary because one might pass in null or undefined and you cannot extract a constructor property from either of these.

It works with any object created via:

  • the Object constructor
  • literals {}

Another neat feature of it, is it's ability to give correct reports for custom classes which make use of Symbol.toStringTag. For example:

class MimicObject {
  get [Symbol.toStringTag]() {
    return 'Object';
  }
}

The problem here is that when calling Object.prototype.toString on an instance of it, the false report [object Object] will be returned:

let fakeObj = new MimicObject();
Object.prototype.toString.call(fakeObj); // -> [object Object]

But checking against the constructor gives a correct result:

let fakeObj = new MimicObject();
fakeObj.constructor === Object; // -> false
David
  • 3,552
  • 1
  • 13
  • 24
5

The best way to find out the REAL type of an object (including BOTH the native Object or DataType name (such as String, Date, Number, ..etc) AND the REAL type of an object (even custom ones); is by grabbing the name property of the object prototype's constructor:

Native Type Ex1:

var string1 = "Test";
console.log(string1.__proto__.constructor.name);

displays:

String

Ex2:

var array1 = [];
console.log(array1.__proto__.constructor.name);

displays:

Array

Custom Classes:

function CustomClass(){
  console.log("Custom Class Object Created!");
}
var custom1 = new CustomClass();

console.log(custom1.__proto__.constructor.name);

displays:

CustomClass
beliha
  • 320
  • 3
  • 6
4

Old question I know. You don't need to convert it. See this function:

function getType( oObj )
{
    if( typeof oObj === "object" )
    {
          return ( oObj === null )?'Null':
          // Check if it is an alien object, for example created as {world:'hello'}
          ( typeof oObj.constructor !== "function" )?'Object':
          // else return object name (string)
          oObj.constructor.name;              
    }   

    // Test simple types (not constructed types)
    return ( typeof oObj === "boolean")?'Boolean':
           ( typeof oObj === "number")?'Number':
           ( typeof oObj === "string")?'String':
           ( typeof oObj === "function")?'Function':false;

}; 

Examples:

function MyObject() {}; // Just for example

console.log( getType( new String( "hello ") )); // String
console.log( getType( new Function() );         // Function
console.log( getType( {} ));                    // Object
console.log( getType( [] ));                    // Array
console.log( getType( new MyObject() ));        // MyObject

var bTest = false,
    uAny,  // Is undefined
    fTest  function() {};

 // Non constructed standard types
console.log( getType( bTest ));                 // Boolean
console.log( getType( 1.00 ));                  // Number
console.log( getType( 2000 ));                  // Number
console.log( getType( 'hello' ));               // String
console.log( getType( "hello" ));               // String
console.log( getType( fTest ));                 // Function
console.log( getType( uAny ));                  // false, cannot produce
                                                // a string

Low cost and simple.

Codebeat
  • 6,501
  • 6
  • 57
  • 99
  • Returns `false` if the test object is `null` or `undefined` – Julian Knight Jun 07 '20 at 13:56
  • or `true` or `false` – Julian Knight Jun 07 '20 at 14:03
  • @JulianKnight false is okay at null or undefined, it is nothing useful. So what is the point? – Codebeat Jun 08 '20 at 14:15
  • your example returns inconsistent data. Some results are the data type and others are the value `false`. How does this help answer the Question? – Julian Knight Jun 13 '20 at 13:30
  • @JulianKnight I don't think you don't understand the purpose of false, you will get false when passing an invalid parameter. This is to tell you something is wrong. It is up to you to throw an exception or not when the result is false. I don't throw exceptions in such functions (like many others lazy programmers do these days), it is more stable and you can decide yourself what to do, if it is critical or not. JS is a language that doesn't predefined static variable types, so it can return anything. That's why you need to check results 'inconsistent data' can be everywhere in JS (by design) – Codebeat Jun 13 '20 at 18:46
  • I think I do understand it thank you. You are throwing a false when you don't need to and this makes the function less generic as I said. `null`, `undefined`, and true/false are all valid JS object types and you could return their type. The question was about the "most accurate" way to return an object type. To answer this question, your function should always return the valid object type. `false` is not a valid answer. – Julian Knight Jun 14 '20 at 20:15
  • 1
    @JulianKnight See changes, is that what you want? If you prefer undefined or "undefined" as result, you can replace the last false if you like to. – Codebeat Jun 16 '20 at 20:32
  • Yes, that is certainly better. Undefined is a valid JavaScript type though so you should have that included. If you want to have a final default, I would actually suggest throwing an error since you should never be able to get that and it would be a much cleaner response in my view. But of course, it ultimately depends on your needs. – Julian Knight Jun 22 '20 at 11:57
  • @JulianKnight: Undefined is not a valid type, it is a way to tell it is not a type of any kind. It gets even worse when defining undefined as something. This is possible, for example var undefined = true. However at normal conditions undefined is nothing, no type, no valid type. About throwing exceptions, at these kind of funcs, that's not the way to do it because if you want to keep your code stable,u have to catch this possible exception to avoid the exception trigger an exception at the wrong place. Only use exeptions at functions that finally produce a result! Don't misunderstnd exceptions – Codebeat Jun 23 '20 at 05:54
2

The best solution is toString (as stated above):

function getRealObjectType(obj: {}): string {
    return Object.prototype.toString.call(obj).match(/\[\w+ (\w+)\]/)[1].toLowerCase();
}

enter image description here

FAIR WARNING: toString considers NaN a number so you must manually safeguard later with Number.isNaN(value).

The other solution suggested, using Object.getPrototypeOf fails with null and undefined

Using constructor

ᄂ ᄀ
  • 5,669
  • 6
  • 43
  • 57
Șerban Ghiță
  • 1,899
  • 20
  • 21
  • For NaN guarding, use Number.isNan(+value) to handle the case where value was defined via new Number(NaN). – Ed_Johnsen Jul 02 '21 at 22:27
  • @Ed_Johnsen thank you for pointing this out! Indeed `Number.isNaN(new Number(NaN)) // false` but `Number.isNaN(+(new Number(NaN))) // true` – Șerban Ghiță Jul 06 '21 at 07:12
0

I put together a little type check utility inspired by the above correct answers:

thetypeof = function(name) {
        let obj = {};
        obj.object = 'object Object'
        obj.array = 'object Array'
        obj.string = 'object String'
        obj.boolean = 'object Boolean'
        obj.number = 'object Number'
        obj.type = Object.prototype.toString.call(name).slice(1, -1)
        obj.name = Object.prototype.toString.call(name).slice(8, -1)
        obj.is = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type === obj[ofType])? true: false
        }
        obj.isnt = (ofType) => {
            ofType = ofType.toLowerCase();
            return (obj.type !== obj[ofType])? true: false
        }
        obj.error = (ofType) => {
            throw new TypeError(`The type of ${name} is ${obj.name}: `
            +`it should be of type ${ofType}`)
        }
        return obj;
    };

example:

if (thetypeof(prop).isnt('String')) thetypeof(prop).error('String')
if (thetypeof(prop).is('Number')) // do something