308

Is there a better way to get the type of a variable in JS than typeof? It works fine when you do:

> typeof 1
"number"
> typeof "hello"
"string"

But it's useless when you try:

> typeof [1,2]
"object"
>r = new RegExp(/./)
/./
> typeof r
"function"

I know of instanceof, but this requires you to know the type beforehand.

> [1,2] instanceof Array
true
> r instanceof RegExp
true

Is there a better way?

Ziki
  • 1,390
  • 1
  • 13
  • 34
Aillyn
  • 23,354
  • 24
  • 59
  • 84
  • 1
    FYI, the `typeof new RegExp(/./); // "function"` issue in Chrome appears to be fixed in Chrome 14. – user113716 Sep 12 '11 at 18:36
  • 2
    possible duplicate of [How do I get the name of an object's type in JavaScript?](http://stackoverflow.com/questions/332422/how-do-i-get-the-name-of-an-objects-type-in-javascript) – Aillyn Sep 12 '11 at 18:45
  • 1
    Warning: If you're minifying your JS and you're using 'custom objects' such as typescript classes then some of the answers below will end up giving you the obfuscated function name such as `n` instead of the expected original name. eg `constructor.name` might give you `n` instead of the expected full name – Simon_Weaver Mar 08 '17 at 00:18

14 Answers14

259

Angus Croll recently wrote an interesting blog post about this -

http://javascriptweblog.wordpress.com/2011/08/08/fixing-the-javascript-typeof-operator/

He goes through the pros and cons of the various methods then defines a new method 'toType' -

var toType = function(obj) {
  return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}
ipr101
  • 24,096
  • 8
  • 59
  • 61
  • 3
    Interesting side note here - this could potentially break where a "host" object is passed to the function. Internet Explorer `ActiveXObject` instances, for example. – Andy E Sep 12 '11 at 16:04
  • If you created your own object type (prototypal inheritance), how could you get it to return a more specific value than "object"? This was also raised as an issue [here](http://stackoverflow.com/questions/8672204/can-you-set-the-internal-class-property-of-an-ecmascript-object), but was never addressed. – EleventyOne Jul 27 '13 at 19:30
  • 1
    If your type naming contains : or _ you may extend this regex to `match(/\s([a-z,_,:,A-Z]+)/)` – omni Jun 20 '14 at 16:58
  • 7
    The regex would also need to include numbers for things like `Uint8Array`. Instead, consider the more general `match(/\s([^\]]+)/)` which should handle anything. – Lily Finley Jan 19 '15 at 22:43
  • 2
    I'd suggest to use the `\w` word operator instead… – kaiser Jun 01 '16 at 00:32
  • Nice, except that I got better results with the function defined a bit more conventionally. – David A. Gray Apr 14 '18 at 22:17
65

You can try using constructor.name.

[].constructor.name
new RegExp().constructor.name

As with everything JavaScript, someone will eventually invariably point that this is somehow evil, so here is a link to an answer that covers this pretty well.

An alternative is to use Object.prototype.toString.call

Object.prototype.toString.call([])
Object.prototype.toString.call(/./)
Community
  • 1
  • 1
Alex Turpin
  • 46,743
  • 23
  • 113
  • 145
  • 11
    `name` is an extension to ECMAScript and will not work in all browsers. – Andy E Sep 12 '11 at 15:43
  • 31
    Answer up-voted due to confirming suspicion that everything done in javascript is somehow evil. – lilHar Nov 28 '15 at 22:06
  • 4
    EVIL: if you're minifying your code then `constructor.name` can likely end up being something like `n` instead of the object name you expect. So watch out for that - especially if you only minify in production. – Simon_Weaver Mar 08 '17 at 00:08
  • `null` and `undefined` unfortunately don't have a constructor. – Andrew Grimm Apr 17 '18 at 02:42
  • 3
    JavaScript itself is evil. And silly. The fact that this question has no good answer is the living proof of that. – user2173353 Nov 06 '18 at 09:49
52

You may find the following function useful:

function typeOf(obj) {
  return {}.toString.call(obj).split(' ')[1].slice(0, -1).toLowerCase();
}

Or in ES7 (comment if further improvements)

const { toString } = Object.prototype;

function typeOf(obj) {
  const stringified = obj::toString();
  const type = stringified.split(' ')[1].slice(0, -1);
      
  return type.toLowerCase();
}

Results:

typeOf(); //undefined
typeOf(null); //null
typeOf(NaN); //number
typeOf(5); //number
typeOf({}); //object
typeOf([]); //array
typeOf(''); //string
typeOf(function () {}); //function
typeOf(/a/) //regexp
typeOf(new Date()) //date
typeOf(new Error) //error
typeOf(Promise.resolve()) //promise
typeOf(function *() {}) //generatorfunction
typeOf(new WeakMap()) //weakmap
typeOf(new Map()) //map
typeOf(async function() {}) //asyncfunction

Thanks @johnrees for notifying me of: error, promise, generatorfunction

Vix
  • 1,046
  • 10
  • 16
42

A reasonably good type capture function is the one used by YUI3:

var TYPES = {
    'undefined'        : 'undefined',
    'number'           : 'number',
    'boolean'          : 'boolean',
    'string'           : 'string',
    '[object Function]': 'function',
    '[object RegExp]'  : 'regexp',
    '[object Array]'   : 'array',
    '[object Date]'    : 'date',
    '[object Error]'   : 'error'
},
TOSTRING = Object.prototype.toString;

function type(o) {
    return TYPES[typeof o] || TYPES[TOSTRING.call(o)] || (o ? 'object' : 'null');
};

This captures many of the primitives provided by javascript, but you can always add more by modifying the TYPES object. Note that typeof HTMLElementCollection in Safari will report function, but type(HTMLElementCollection) will return object

Nick Husher
  • 1,909
  • 11
  • 13
16

Also we can change a little example from ipr101

Object.prototype.toType = function() {
  return ({}).toString.call(this).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
}

and call as

"aaa".toType(); // 'string'
greymaster
  • 169
  • 2
  • 5
  • 8
    Remember kids, manipulating JavaScript base objects can lead to compatibility and other weird issues. Try to use this sparingly in both libraries and larger projects! – Alexander Craggs Oct 31 '16 at 20:47
12

one line function:

function type(obj) {
    return Object.prototype.toString.call(obj).replace(/^\[object (.+)\]$/,"$1").toLowerCase()
}

this give the same result as jQuery.type()

Yukulélé
  • 15,644
  • 10
  • 70
  • 94
6

You can apply Object.prototype.toString to any object:

var toString = Object.prototype.toString;

console.log(toString.call([]));
//-> [object Array]

console.log(toString.call(/reg/g));
//-> [object RegExp]

console.log(toString.call({}));
//-> [object Object]

This works well in all browsers, with the exception of IE - when calling this on a variable obtained from another window it will just spit out [object Object].

Andy E
  • 338,112
  • 86
  • 474
  • 445
  • 1
    @Xeon I think the hatred against IE is a bit too much. IE9 is a much better browser than its predecessors. – Aillyn Sep 12 '11 at 15:58
  • 4
    @pessimopoppotamus but nobody who would update their browser to IE9 uses IE. – Alex Turpin Sep 12 '11 at 16:01
  • 1
    IE 9 is a step in the right direction, IE 10 is looking pretty good. Incidentally, the bug I mentioned was a regression in IE 9 - they fixed it in IE 8. – Andy E Sep 12 '11 at 16:05
4

My 2¢! Really, part of the reason I'm throwing this up here, despite the long list of answers, is to provide a little more all in one type solution and get some feed back in the future on how to expand it to include more real types.

With the following solution, as aforementioned, I combined a couple of solutions found here, as well as incorporate a fix for returning a value of jQuery on jQuery defined object if available. I also append the method to the native Object prototype. I know that is often taboo, as it could interfere with other such extensions, but I leave that to user beware. If you don't like this way of doing it, simply copy the base function anywhere you like and replace all variables of this with an argument parameter to pass in (such as arguments[0]).

;(function() {  //  Object.realType
    function realType(toLower) {
        var r = typeof this;
        try {
            if (window.hasOwnProperty('jQuery') && this.constructor && this.constructor == jQuery) r = 'jQuery';
            else r = this.constructor && this.constructor.name ? this.constructor.name : Object.prototype.toString.call(this).slice(8, -1);
        }
        catch(e) { if (this['toString']) r = this.toString().slice(8, -1); }
        return !toLower ? r : r.toLowerCase();
    }
    Object['defineProperty'] && !Object.prototype.hasOwnProperty('realType')
        ? Object.defineProperty(Object.prototype, 'realType', { value: realType }) : Object.prototype['realType'] = realType;
})();

Then simply use with ease, like so:

obj.realType()  //  would return 'Object'
obj.realType(true)  //  would return 'object'

Note: There is 1 argument passable. If is bool of true, then the return will always be in lowercase.

More Examples:

true.realType();                            //  "Boolean"
var a = 4; a.realType();                    //  "Number"
$('div:first').realType();                   // "jQuery"
document.createElement('div').realType()    //  "HTMLDivElement"

If you have anything to add that maybe helpful, such as defining when an object was created with another library (Moo, Proto, Yui, Dojo, etc...) please feel free to comment or edit this and keep it going to be more accurate and precise. OR roll on over to the GitHub I made for it and let me know. You'll also find a quick link to a cdn min file there.

SpYk3HH
  • 22,272
  • 11
  • 70
  • 81
  • If you do `(null).realType()` it gives `Uncaught TypeError: Cannot read properties of null (reading 'realType')` – Capt 171 Oct 20 '21 at 04:33
3
function getType(obj) {
    if(obj && obj.constructor && obj.constructor.name) {
        return obj.constructor.name;
    }
    return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}

In my preliminary tests, this is working pretty well. The first case will print the name of any object created with "new", and the 2nd case should catch everything else.

I'm using (8, -1) because I'm assuming that the result is always going to start with [object and end with ] but I'm not certain that's true in every scenario.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
3

This version is a more complete one:

const typeOf = obj => {
  let type = ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1]
  if (type === 'Object') {
    const results = (/^(function|class)\s+(\w+)/).exec(obj.constructor.toString())
    type = (results && results.length > 2) ? results[2] : ''
  }
  return type.toLowerCase()
}

Now not only you can have these results: (as they've been answered here)

undefined or empty -> undefined
null -> null
NaN -> number
5 -> number
{} -> object
[] -> array
'' -> string
function () {} -> function
/a/ -> regexp
new Date() -> date
new Error -> error
Promise.resolve() -> promise
function *() {} -> generatorfunction
new WeakMap() -> weakmap
new Map() -> map

But also you can get the type of every instance or object you construct from classes or functions: (which is not valid between other answers, all of them return object)

class C {
  constructor() {
    this.a = 1
  }
}

function F() {
  this.b = 'Foad'
}

typeOf(new C()) // -> c
typeOf(new F()) // -> f
F.NiX
  • 1,457
  • 3
  • 12
  • 20
  • Hi, I see maybe one possible improvement (but questionable) -> if the function return custom defined classname/objectName it should be probably case-sensitive, return exact name. – JsonKody Oct 15 '21 at 08:25
2

I guess the most universal solution here - is to check for undefined and null first, then just call constructor.name.toLowerCase().

const getType = v =>
  v === undefined
    ? 'undefined'
    : v === null
      ? 'null'
      : v.constructor.name.toLowerCase();




console.log(getType(undefined)); // 'undefined'
console.log(getType(null)); // 'null'
console.log(getType('')); // 'string'
console.log(getType([])); // 'array'
console.log(getType({})); // 'object'
console.log(getType(new Set())); // `set'
console.log(getType(Promise.resolve())); // `promise'
console.log(getType(new Map())); // `map'
Arsenowitch
  • 401
  • 5
  • 22
  • As far I know - the `.constructor.name` always contains the string value. For undefined/null we have the appropriate strings returned by the function. – Arsenowitch Nov 25 '19 at 13:54
  • Thanks - deleting my comments… – Bergi Nov 26 '19 at 16:25
  • 1
    Since `constructor` is an inherited property, this breaks for objects created with `Object.create(null)`. Simple enough to fix, but what to call it? "[object null]" ? – traktor May 28 '20 at 01:13
2

I've made this function:

( You should name it more unique so it doesn't collide with some other global name. )

function type(theThing) {
    return Object.prototype.toString.call(theThing).match(/\s([\w]+)/)[1].toLowerCase()
}
type({})           //-> 'object'
type([])           //-> 'array'
type(function(){}) //-> 'function'
    
type(null)         //-> 'null'
type(undefined)    //-> 'undefined
type(true)         //-> 'boolean'
type('hello')      //-> 'string'
type(42)           //-> 'number'

type(Symbol())     //-> 'symbol'
type(/abc/)        //-> 'regexp'
type(new Set())    //-> 'set'
// etc ...

PS: F.NiX above made more robust version that also tell you the name of your custom objects made from Class or constructor function.

JsonKody
  • 570
  • 3
  • 13
0

https://npmjs.com/package/advanced-type

I created a package for this purpose.

Human Friend
  • 135
  • 1
  • 4
  • 11
-3

typeof condition is used to check variable type, if you are check variable type in if-else condition e.g.

if(typeof Varaible_Name "undefined")
{

}
4b0
  • 21,981
  • 30
  • 95
  • 142
MzkZeeshan
  • 313
  • 4
  • 3