JavaScript Get Type of Object: Robust Type Detection
Learn how to reliably detect JavaScript object types with toString, constructor, and modern checks. This guide covers null, arrays, maps, sets, dates, and custom objects.

To reliably detect an object's type in JavaScript, avoid relying on typeof for everything. Use Object.prototype.toString.call(value) or a small getType helper to reveal the true tag (e.g., 'array', 'date', 'map', 'set', 'null') or the constructor name. This works consistently across frames and handles null, arrays, dates, and custom objects well.
Understanding the problem: Why typeof is not enough
In JavaScript, the typeof operator is great for primitive types, but it falls short for objects. For example, typeof null returns object, and typeof [] also returns object. This makes it hard to distinguish arrays, dates, maps, sets, or user-defined objects using a single operator. In real code, you need a more precise detector that works across different execution contexts and frames.
console.log(typeof null); // object
console.log(typeof []); // objectNote: The quirk comes from early JavaScript design; null is a primitive that is falsy, while arrays are objects. The goal is a consistent method for all common cases and edge cases.
Detecting type with Object.prototype.toString
A robust approach is to inspect the internal [[Class]] tag via Object.prototype.toString.call(value): it returns [object Type], from which you can extract Type in a uniform way.
function getTag(v){
return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
}
console.log(getTag(null)); // null
console.log(getTag([])); // array
console.log(getTag({})); // objectLine-by-line:
- getTag uses toString.call to fetch the internal tag
- slice(8, -1) trims to the Type word
- toLowerCase standardizes the output for comparisons
Variants:
- You can return the raw tag or map it to your own canonical names like array/date/map.
Distinguishing common built-in types (Array, Map, Set, Date)
Once you have a canonical tag, you can build helpers for frequent types. Arrays, Maps, Sets, and Dates are common candidates.
console.log(Array.isArray([])); // true
console.log(getTag(new Map())); // map
console.log(getTag(new Set())); // set
console.log(getTag(new Date())); // dateAdditional checks:
- Map and Set have distinct internal structures; toString tag works reliably
- For plain objects, getTag returns 'object' unless you wrap or subclass
If you need to differentiate objects created in different frames, rely on the canonical tag rather than constructor names.
Using constructor.name and prototype checks
Another strategy is to peek at the constructor name when available. This helps identify user-defined classes but has caveats with cross-frame objects.
function ctorName(v){ return v?.constructor?.name ?? 'unknown'; }
console.log(ctorName([])); // Array
console.log(ctorName(/regex/)); // RegExpPrototype checks offer another route:
if (v != null && v.constructor && v.constructor.name) {
// usable in debugging or logging
}Tips:
- constructor.name is not guaranteed in all environments; prefer getTag for reliability
- For cross-frame objects, the constructor may be a different function object, making name comparisons brittle.
A robust getType helper and edge cases
To unify detection, implement a single getType helper that handles common edge cases: null, undefined, and the full range of built-in and custom types.
function getType(v){
if (v === null) return 'null';
if (v === undefined) return 'undefined';
return Object.prototype.toString.call(v).slice(8, -1).toLowerCase();
}
console.log(getType(null)); // null
console.log(getType([])); // array
console.log(getType(new Map())); // mapPreferred usage:
- Use getType in all type checks to ensure consistency across code paths
- Combine with specific checks (isArray, isDate) when necessary
Benefits include predictable behavior across workers, iframes, and different execution contexts.
Practical usage patterns and performance considerations
In real apps, you’ll often need to branch behavior based on value types. A small, well-documented helper saves time and avoids subtle bugs. Use getType for most checks, while keeping fast-path detectors like Array.isArray for arrays. As you scale, you can compose predicates like isDate, isMap, or isPlainObject around the same core getType logic.
function isDate(v){ return getType(v) === 'date'; }
function isPlainObject(v){ return getType(v) === 'object' && v.constructor === Object; }Performance note: avoid repeated toString calls in hot loops. Cache results when you expect to check the type of the same value multiple times in tight loops. In practice, the single getType call is fast enough for most UI-level work, but micro-benchmarks can guide your optimizations in performance-critical code.
],
prerequisites
Steps
Estimated time: 30-60 minutes
- 1
Define a single-purpose helper
Create a small utility that returns a normalized type tag using Object.prototype.toString.call and slice. Document what each tag means so future readers understand the mapping.
Tip: Add JSDoc comments to explain the mapping between tag and category. - 2
Test common values
Run the helper with null, undefined, arrays, dates, maps, sets, and objects. Confirm the outputs align with expectations and adjust the mapping as needed.
Tip: Use a simple test script or a quick console snippet. - 3
Guard edge cases
Explicitly handle null and undefined before other checks to avoid runtime errors. Ensure your code does not throw when values come from different frames.
Tip: Order matters; check null/undefined first. - 4
Integrate and document
Incorporate getType into your utility module and expose isArray/isDate helpers for readability. Document usage patterns in a README for teammates.
Tip: Keep the helpers small and focused. - 5
Benchmark and optimize
Run a micro-benchmark on type checks in hot paths and cache results if needed. Consider refactoring for minimal allocations in tight loops.
Tip: Profile with real data and adjust as necessary.
Prerequisites
Required
- JavaScript knowledge (ES6+)Required
- Required
Optional
- Code editor (e.g., VS Code)Optional
- Basic command line knowledgeOptional
- Access to a browser console or Node REPLOptional
Keyboard Shortcuts
| Action | Shortcut |
|---|---|
| CopyCopy code blocks or lines | Ctrl+C |
| PastePaste into editor or console | Ctrl+V |
| Find in documentSearch within the article | Ctrl+F |
| Toggle DevToolsOpen browser dev tools to test snippets | Ctrl+⇧+I |
Questions & Answers
Why does typeof null return 'object' in JavaScript?
This is a historical quirk from early JavaScript design. The typeof operator cannot distinguish null from objects, so null reports as 'object'. The robust approach is to use toString-based tags or constructor checks for real type resolution.
Null shows up as an object because of how the language was designed; use toString-based checks for accurate types.
How do I detect arrays in JavaScript reliably?
The recommended way is Array.isArray(value), which returns true for arrays and false otherwise. If you’re using a generic type detector, you can also check the tag equals 'array'.
Use Array.isArray for a fast, reliable array check.
Is constructor.name reliable across frames?
Not always. Objects created in different frames/iframes can have different constructor functions, so name-based checks can fail across contexts. Prefer a canonical tag via toString-based detection; use constructor.name only as a hint.
Avoid relying on constructor.name when objects come from different frames.
What about user-defined classes?
getType will return the class name via constructor?.name if available. For cross-frame classes, constructor.name may differ, so rely on the canonical tag as the primary detector and use constructor.name as secondary metadata.
Your class instances usually reveal their class name, but frame boundaries can break that.
Does this impact performance in hot code paths?
Type checks are generally fast, but in hot loops you can benchmark getType and cache results if needed. Prefer minimal allocations and reuse the helper to reduce cost.
Type checks are fast, but you can optimize if you’re profiling bottlenecks.
What to Remember
- Use Object.prototype.toString.call to get a reliable type tag
- Handle null and undefined explicitly in type checks
- Prefer Array.isArray for array detection
- Create a single getType helper for consistency
- Cross-frame objects can break constructor.name-based checks