JavaScript · Beginner
JavaScript Data Types: Primitives, Objects and typeof
In short: JavaScript has seven primitive types: string, number, bigint, boolean, undefined, null and symbol. Everything else, including arrays and functions, is an object. Variables have no fixed type; the value does. The typeof operator reports a value's type as a string, with one historical quirk: typeof null is 'object'.
Values have types, variables do not
JavaScript is dynamically typed: a variable can hold a number now and a string later, and nothing is declared in advance. What has a type is the value itself, and the engine decides at run time what an operation means by looking at the types involved. That is why "5" + 1 is the string "51" while "5" * 1 is the number 5. Understanding the types is the way to stop being surprised by that.
The primitive types are values that cannot be changed and are compared by content. A string is text in single quotes, double quotes or backticks. A number is a 64-bit floating-point value used for both integers and decimals; it is exact for whole numbers up to Number.MAX_SAFE_INTEGER (2 to the power 53, minus 1), but decimal fractions such as 0.1 cannot be stored exactly, which produces results like 0.1 + 0.2 being 0.30000000000000004. Two special numbers exist: NaN ("not a number", the result of failed arithmetic) and Infinity. A bigint (ES2020), written with an n suffix like 12n, holds integers of any size. A boolean is true or false. undefined is the value of a variable that has not been assigned and of a missing property; null is a deliberate "no value" that a programmer writes on purpose. A symbol is a unique identifier used as a property key; beginners rarely need it.
Everything else is an object: plain objects, arrays, functions, dates, and so on. Objects are compared by identity, so two separately built arrays with the same contents are not equal. The arrays and objects lessons cover them.
The typeof operator returns the type name as a string. It says "function" for functions and "object" for every other object, including arrays (use Array.isArray() to tell) and, because of a bug preserved since the first version of the language, also for null.
Conversions are explicit or implicit. Number(text) turns a string into a number, giving NaN if the whole string is not numeric; parseInt and parseFloat read a number from the start of a string and ignore the rest. String(value) and value.toString() go the other way. Boolean(value) gives false for the six falsy values (0, "", null, undefined, NaN and false itself; 0n too) and true for everything else, including "0" and an empty array. Input from readline() is always a string, so converting it is the first thing most programs do.
Syntax
const text = "twelve"; // string
const whole = 12; // number (integer)
const part = 12.75; // number (decimal)
const huge = 12345678901234567890n; // bigint
const flag = true; // boolean
let nothingYet; // undefined
const empty = null; // null, on purpose
typeof value // "string", "number", "bigint", "boolean",
// "undefined", "object", "function", "symbol"
Number("42") String(42) Boolean("") parseInt("42px", 10) parseFloat("2.5kg")Pass 10 as the second argument of parseInt so the string is always read as decimal.
Types at a glance
| Type | Example literal | typeof gives |
|---|---|---|
| string | "harbour" | "string" |
| number | 42, 3.5, NaN, Infinity | "number" |
| bigint | 9007199254740993n | "bigint" |
| boolean | true | "boolean" |
| undefined | undefined | "undefined" |
| null | null | "object" (quirk) |
| symbol | Symbol("id") | "symbol" |
| object | { a: 1 }, [1, 2] | "object" |
| function | function () {} | "function" |
typeof on each kind of value
One line per type; note the two lines that both say object.
console.log(typeof "Ferry");
console.log(typeof 42);
console.log(typeof 3.75);
console.log(typeof 9007199254740993n);
console.log(typeof true);
console.log(typeof undefined);
console.log(typeof null);
console.log(typeof { id: 1 });
console.log(typeof [1, 2, 3]);
console.log(typeof function () {});
console.log(Array.isArray([1, 2, 3]), Array.isArray({ id: 1 }));Output
string number number bigint boolean undefined object object object function true false
Integers and decimals are both number; there is no separate integer type unless you use a bigint. null reports object even though it is a primitive: the check was wrong in the first JavaScript engine and fixing it would break existing programs, so it stays. Arrays are objects too, which is why Array.isArray() exists. Functions are objects as well, but typeof singles them out as function, which is convenient.
Numbers: precision, safe integers, NaN and bigint
The lines show what floating point can and cannot represent.
console.log(0.1 + 0.2);
console.log(0.1 + 0.2 === 0.3);
console.log((0.1 + 0.2).toFixed(2));
console.log(Number.MAX_SAFE_INTEGER);
console.log(9007199254740992 + 1);
console.log(9007199254740992n + 1n);
console.log(10 / 0);
console.log("abc" * 2);
console.log(Number.isNaN("abc" * 2));
console.log(Number.isInteger(7.0), Number.isInteger(7.5));Output
0.30000000000000004 false 0.30 9007199254740991 9007199254740992 9007199254740993n Infinity NaN true true false
Neither 0.1 nor 0.2 has an exact binary representation, so their sum carries a tiny error and does not equal the stored value of 0.3. toFixed(2) rounds to two decimals and returns a string, which is the right tool for displaying money. Above the safe integer limit, adding 1 to a number can leave it unchanged because the next representable value is two away; a bigint has no such limit. Dividing by zero gives Infinity rather than an error, and arithmetic on text that is not numeric gives NaN. NaN is the only value not equal to itself, so test for it with Number.isNaN().
Converting input between string and number
The input lines are 18 and 2.5. Watch what + does before and after the conversion.
const line = readline();
console.log(typeof line, line);
const count = Number(line);
console.log(typeof count, count + 1);
console.log(line + 1);
const price = parseFloat(readline());
console.log(price * count);
console.log(String(count) + " boxes");
console.log(Number(""), Number(" 7 "), Number("7 boxes"), parseInt("7 boxes", 10));
console.log(Boolean(0), Boolean(""), Boolean("0"), Boolean([]));Input given to the program: 18 ↵ 2.5
Output
string 18 number 19 181 45 18 boxes 0 7 NaN 7 false false true true
readline() gives the string "18"; adding 1 to the string joins the characters, adding 1 to the converted number gives 19. Number() accepts surrounding spaces and treats an empty string as 0, but refuses a string with trailing text, returning NaN; parseInt stops at the first character that is not a digit and returns 7. The last line shows the boolean conversion: the number 0 and the empty string are falsy, but the string "0" and an empty array are truthy, because any non-empty string and any object count as true.
Common mistakes
Doing arithmetic on input without converting it
Why it goes wrong: Everything read from input is a string, so
readline() + 5concatenates. Comparisons like"10" < "9"are also done character by character on strings, which gives true.Fix: Convert with
Number()(orparseInt/parseFloat) as soon as you read a value that is meant to be numeric.JavaScript · fixconst qty = Number(readline()); console.log(qty + 5);Comparing decimals with ===
Why it goes wrong:
0.1 + 0.2 === 0.3is false because of floating-point rounding, so a comparison that should pass fails for no visible reason.Fix: Compare with a tolerance:
Math.abs(a - b) < 1e-9, or work in whole units such as cents.JavaScript · fixconst a = 0.1 + 0.2; console.log(Math.abs(a - 0.3) < 1e-9); // trueUsing typeof to test for an object
Why it goes wrong:
typeof value === "object"is also true fornull, so code that then readsvalue.namethrows TypeError: Cannot read properties of null.Fix: Check for null as well:
value !== null && typeof value === "object". For arrays, useArray.isArray(value).Testing for NaN with ===
Why it goes wrong:
NaN === NaNis false, soif (result === NaN)can never be true.Fix: Use
Number.isNaN(result). The older globalisNaN()first converts its argument, soisNaN("abc")is true even though"abc"is a string;Number.isNaNonly reports genuine NaN values.
Where you use this
Reading and validating input is where types matter most. A program that reads a quantity must convert it, and a robust one checks the result before using it: if Number(line) gives NaN, the line was not a number and the program should say so rather than print NaN in its answer. The same thinking applies to data from a web form or a file, which always arrives as text. Knowing which values are falsy also lets you write short checks such as if (name) for "a non-empty name was given", as long as you remember that 0 is falsy too and use an explicit comparison when zero is a valid value.
const raw = readline();
const amount = Number(raw);
if (raw.trim() === "" || Number.isNaN(amount)) {
console.log("not a number: " + raw);
} else {
console.log(amount * 2);
}Key points
- Seven primitives: string, number, bigint, boolean, undefined, null, symbol; everything else is an object.
- One number type for integers and decimals; whole numbers are exact only up to 2^53 - 1.
0.1 + 0.2is not exactly 0.3; round for display withtoFixed()and compare with a tolerance.typeof nullis"object"andtypeof []is"object"; useArray.isArray()for arrays.- Input is always a string; convert with
Number(),parseInt()orparseFloat(). - Falsy values:
0,0n,"",null,undefined,NaN,false. Everything else is truthy.
Try it yourself
The starter prints the input text twice, joined. Convert raw to a number so the program prints the doubled quantity, then print the typeof of the original value and of the converted value on one line, separated by a space.
const raw = readline();
// convert raw to a number, then print the doubled value and the two types
console.log(raw + raw);
2142
string numberconst raw = readline();
const qty = Number(raw);
console.log(qty + qty);
console.log(typeof raw, typeof qty);
Practise this
Exercises for this lesson are in the JavaScript practice set.
Open the JavaScript playground
Related lessons
- Variables in JavaScript: let, const and varHow to declare variables in JavaScript with let, const and var, what block scope and hoisting mean, and why const is the default choice.8 min
- JavaScript Operators: Arithmetic, Comparison and LogicJavaScript's arithmetic, assignment, comparison and logical operators, why === beats ==, how + behaves with strings, and what ?? and the ternary do.9 min
- JavaScript Strings: Methods, Template Literals and SlicingJavaScript string basics: quotes, template literals, length and indexing, slice, split and join, trim, replace and case methods, and why strings never change.10 min
- JavaScript Arrays: Creating, Indexing and Changing ListsHow JavaScript arrays store ordered lists: indexing, length, push and pop, slice versus splice, sorting numbers correctly, and map, filter and reduce.10 min
- JavaScript Objects: Properties, Methods and NestingHow JavaScript objects group data as key-value properties: dot and bracket access, adding and deleting, methods with this, nesting and reference behaviour.10 min
Frequently asked questions
Why is typeof null "object" in JavaScript?
In the first JavaScript engine, values carried a small type tag, and the tag for objects happened to be the same as the representation of null. typeof read the tag and answered "object". By the time the mistake was noticed, programs depended on it, and a proposal to change the result was rejected because it would break existing websites. Test for null with value === null.
What is the difference between undefined and null?
undefined is what JavaScript itself uses for "no value has been given": an unassigned variable, a missing object property, a missing function argument, or the result of a function with no return. null is a value a programmer assigns deliberately to say "empty on purpose". They are equal under == but not under ===, and typeof reports them differently.
How do I check whether a string is a valid number?
Convert it with Number() and test the result with Number.isNaN(). Also reject an empty or whitespace-only string, because Number("") returns 0 rather than NaN. Avoid the older global isNaN(), which converts non-numeric strings first and so reports true for any text.
How this page was checked. Every program on it was run with Node.js 22 at build time; runs in your browser in an isolated Web Worker by the publishing checks, and the output shown is what it printed. The Run buttons execute the same code on your device; nothing you type is sent anywhere.