The Eight Types

Mental model: Seven primitives and one object type, and typeof is your first question about any value.

Level: beginner · about 11 minutes

console.log(typeof 'text');       // 'string'
console.log(typeof 42);           // 'number'
console.log(typeof 9007199254740993n); // 'bigint'
console.log(typeof true);         // 'boolean'
console.log(typeof undefined);    // 'undefined'
console.log(typeof Symbol('id')); // 'symbol'
console.log(typeof null);         // 'object'  <-- wrong
console.log(typeof { a: 1 });     // 'object'

Run this before you read anything. One of the eight answers is wrong, and has been since 1995.

Every value in JavaScript is one of eight types: seven primitives (string, number, bigint, boolean, undefined, symbol, null) and object. Arrays, functions, dates, Maps and class instances are not extra types. They are objects with different equipment.

TypeExample valuetypeof saysWorth knowing
string'hi''string'immutable, UTF-16 under the hood
number42, 3.14, NaN'number'one type for integers and decimals
bigint9007199254740993n'bigint'exact integers of any size
booleantrue'boolean'only two values ever
undefinedundefined'undefined'the absence you did not choose
symbolSymbol('id')'symbol'a guaranteed unique key
nullnull'object'the absence you did choose, and a broken typeof
object{}, [], () => {}'object' or 'function'everything with properties

The typeof null bug

const value = null;

console.log(typeof value === 'object'); // true, and useless to you
console.log(value === null);            // true, the honest check
console.log(value == undefined);        // true, both count as "no value"

Arrays are objects, so ask Array.isArray

console.log(typeof [1, 2, 3]);         // 'object', not helpful
console.log(Array.isArray([1, 2, 3]));  // true
console.log(Array.isArray('abc'));      // false, even though it has a length
console.log(Array.isArray({ 0: 'a' })); // false, array-like is not an array
`typeof x === "object"`
true for null, arrays, dates, Maps and plain objects alike
`Array.isArray(x)`
the only check for arrays you should write
`x === null`
the only check for null you should write
`Number.isNaN(x)`
the only check for NaN you should write

Wrapper objects and autoboxing

A primitive has no properties, so "ada".toUpperCase() looks impossible. When you use a method on a primitive, the engine wraps it in a temporary object, reads the method from that object, runs it, and throws the wrapper away. That is autoboxing, and it happens on every method call.

const name = 'ada';
console.log(name.toUpperCase()); // 'ADA' via a temporary String wrapper
console.log(typeof name);        // 'string', still a primitive

const boxed = new String('ada');
console.log(typeof boxed);              // 'object'
console.log(boxed == 'ada', boxed === 'ada'); // true false

One more oddity: typeof has nine possible answers, not eight, because functions get their own. That is a convenience, not a ninth type. A function is an object you can call.

console.log(typeof NaN);

NaN stands for "not a number", and its type is number. It is the numeric value that represents a failed numeric operation, so it has to live in the number type. Ask Number.isNaN(x) when you want to detect it, because NaN === NaN is false.

Try it yourself

Type probe

const values = ['text', 42, 10n, true, undefined, null, Symbol('id'), { a: 1 }, [1, 2], () => {}];

for (const value of values) {
  console.log(String(value).padEnd(14), typeof value);
}

console.log('arrays found:', values.filter(Array.isArray).length);
console.log('nulls found:', values.filter((v) => v === null).length);

Add your own values: a Date, a Map, NaN, a class instance. How many of them report "object"?

Exercises

Write an honest describeType

Write describeType(value) which returns the type as a lowercase string, but fixes what typeof gets wrong: return "null" for null and "array" for arrays. Everything else keeps its typeof answer.

Check yourself

What does this log?
2 — Two values pass: null (because of the 1995 bug) and [] (because arrays are objects). Numbers, strings and undefined all report their own type. This is exactly why a typeof v === "object" guard is rarely what you want.
Which check reliably tells you a value is an array?
Array.isArray(value) — typeof never returns "array". A length property is present on strings and on any array-like object, and instanceof Object is true for every object. Array.isArray is the purpose-built answer and it works across realms, such as an array from an iframe.
Why can you call .toUpperCase() on a primitive string, which has no properties?
The engine wraps the primitive in a temporary object, runs the method, then discards the wrapper — That temporary wrapping is autoboxing. The wrapper is thrown away immediately, which is why const s = "a"; s.custom = 1; console.log(s.custom) gives undefined: you set a property on an object that no longer exists.

Common mistakes

  • Using typeof value === "object" as an object check. It is also true for null and for every array.
  • Writing new Number(5) or new String("a"). You get an object that fails === against the primitive.
  • Expecting a ninth type for arrays or functions. Both are objects; typeof just makes an exception for functions.

Takeaways

  • Seven primitives plus object. Arrays, functions and dates are all objects.
  • typeof null === "object" is a 1995 bug that will never be fixed. Compare with === null.
  • Array.isArray is the only array check worth writing.
  • Methods on primitives work through a temporary wrapper object that is thrown away at once.