Skip to content


Subversion checkout URL

You can clone with
Download ZIP


_.toType for robust type checking, improvement of typeof #269

wants to merge 2 commits into from

4 participants


Adds _.toType() method, borrowed from Angus Croll's article on "Fixing the JavaScript typeof operator" ( Includes some tests, too.

Normally I only ever use typeof whatever !== "undefined" since as Angus points out, typeof is pretty useless for many other cases, for example:

typeof null; // "object"
typeof {a: 4}; // "object"
typeof [1, 2, 3]; // "object"
(function() { return typeof arguments })(); // "object"
typeof new ReferenceError; // "object"
typeof new Date; // "object"
typeof /a-z/; // "object"
typeof Math; // "object"
typeof JSON; // "object"
typeof new Number(4); // "object"
typeof new String("abc"); // "object"
typeof new Boolean(true); // "object"


_.toType({a: 4}); // "Object"
_.toType([1, 2, 3]); // "Array"
(function() { return _.toType(arguments) })(); // "Arguments"
_.toType(new ReferenceError); // "Error"
_.toType(new Date); // "Date"
_.toType(/a-z/); // "RegExp"
_.toType(Math); // "Math"
_.toType(JSON); // "JSON"
_.toType(new Number(4)); // "Number"
_.toType(new String("abc")); // "String"
_.toType(new Boolean(true)); // "Boolean"

NB. It might make more sense to return types as lowercase in some scenarios (for example "Undefined" or "Null", or where the difference between "string" and "String" might cause issues) - not sure really. Perhaps the function could return lowercase by default, and add an option eg. preserveCase = preserveCase || false


Add a test case for Function type, something like ok(_.toType(function(){}) === "Function", 'function(){} is a Function');. Anyway, I've test it and it's working.


Re: above commit, adds two more tests for Function types.


Can you produce a use case for this? I'm failing to see how this is useful considering underscore already has the various _.isX methods. You say this "fixes" typeof, but typeof only has two valid uses, one of which can't be emulated (testing if a variable has been declared without throwing a ReferenceError) and another (existence of [[Call]]) which is already covered by _.isFunction ... mostly (at least as well as this would cover it).

edit: Oh, I see. I should have looked at the code. You're just pulling the [[Class]]. And what practical use does that have?


Yep -- I'm afraid this method goes almost entirely counter to the spirit of Underscore: Looking for explicit types is a fool's game in JavaScript, because they're very weak (new Number vs. 5), and good for almost nothing.

If you want to dispatch differently based on the type being passed as an argument to a function ... just check for that type: which the[Type] family of functions is already able to do far more efficiently than a toString test.

@jashkenas jashkenas closed this

Fair play, cheers for the feedback.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
Showing with 34 additions and 0 deletions.
  1. +23 −0 test/objects.js
  2. +11 −0 underscore.js
23 test/objects.js
@@ -238,6 +238,29 @@ $(document).ready(function() {
ok(_.isUndefined(iUndefined), 'even from another frame');
+ test("objects: toType", function() {
+ ok(_.toType(1) === "number", '1 is a "number"');
+ ok(_.toType(-Infinity) === "number", '-Infinity is a "number"');
+ ok(_.toType(new Number) === "Number", '`new Number` is a "Number"');
+ ok(_.toType(NaN) === "Number", 'NaN is a "Number" (for realz)');
+ ok(_.toType(function(){}) === "Function", '`function(){}` is a "Function"');
+ ok(_.toType(window.alert) === "Function", '`window.alert` is a "Function"');
+ ok(_.toType(null) === "Null", 'null is type "Null"');
+ ok(_.toType(undefined) === "Undefined", 'while we\'re at it, undefined is "Undefined"');
+ ok(_.toType("o hai") === "string", '"o hai" is a "string"');
+ ok(_.toType(new String("o hai")) === "String", 'but `new String("o hai") is a "String"`');
+ ok(_.toType(false) === "Boolean", 'false is "Boolean"');
+ ok(_.toType(new Boolean(true)) === "Boolean", '`new Boolean(true)` is also "Boolean"');
+ ok(_.toType(/a-z/) === "RegExp", '/a-z/ is a "RegExp"');
+ ok(_.toType({a: 4}) === "Object", '`{a:4}` is type "Object"');
+ ok(_.toType([1, 2, 3]) === "Array", '`[1,2,3]` is type "Array"');
+ ok((function() {return _.toType(arguments)})() == "Arguments", 'even the arguments object gets a type of its own, "Arguments"');
+ ok(_.toType(new ReferenceError) === "Error", '`new ReferenceError` is type "Error"');
+ ok(_.toType(new Date) === "Date", 'new Date is a "Date"');
+ ok(_.toType(Math) === "Math", 'Math is type "Math"');
+ ok(_.toType(JSON) === "JSON", 'JSON is, of course, "JSON" type');
+ });
if (window.ActiveXObject) {
test("objects: IE host objects", function() {
var xml = new ActiveXObject("Msxml2.DOMDocument.3.0");
11 underscore.js
@@ -719,6 +719,17 @@
return obj === void 0;
+ // Robust type detection, uses a regex to match the return from native object.toString()
+ // Falls back to native 'typeof' for truthy primitive values (credit: Angus Croll)
+ //
+ _.toType = function(obj) {
+ if((function() {return obj && (obj !== this)}).call(obj)) {
+ return typeof obj;
+ }
+ return ({})\s([a-z|A-Z]+)/)[1];
+ };
// Utility Functions
// -----------------
Something went wrong with that request. Please try again.