Aillyn
Aillyn

Reputation: 23813

Better way to get type of a Javascript variable?

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?

Upvotes: 309

Views: 413391

Answers (14)

Human Friend
Human Friend

Reputation: 175

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

I created a package for this purpose.

Upvotes: 0

JsonKody
JsonKody

Reputation: 717

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.

Upvotes: 2

Vix
Vix

Reputation: 1066

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

Upvotes: 53

F.NiX
F.NiX

Reputation: 1505

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

Upvotes: 3

MzkZeeshan
MzkZeeshan

Reputation: 363

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")
{

}

Upvotes: -3

Arsenowitch
Arsenowitch

Reputation: 401

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'

Upvotes: 2

Yukulélé
Yukulélé

Reputation: 17132

one line function:

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

this give the same result as jQuery.type()

Upvotes: 13

SpYk3HH
SpYk3HH

Reputation: 22580

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.

Upvotes: 4

mpen
mpen

Reputation: 283355

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.

Upvotes: 3

greymaster
greymaster

Reputation: 169

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'

Upvotes: 16

ipr101
ipr101

Reputation: 24236

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()
}

Upvotes: 263

Nick Husher
Nick Husher

Reputation: 1884

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

Upvotes: 42

Alex Turpin
Alex Turpin

Reputation: 47776

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(/./)

Upvotes: 68

Andy E
Andy E

Reputation: 344803

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].

Upvotes: 6

Related Questions