JavaScript Data Types and Type System
1. Primitive Types (string, number, boolean, null, undefined, symbol, bigint)
| Type | Description | typeof Result | Default Value | Wrapper Object |
|---|---|---|---|---|
| string | Immutable sequence of Unicode characters | "string" |
"" (empty string) |
String |
| number | 64-bit IEEE 754 floating point (±1.8×10308) | "number" |
0 |
Number |
| boolean | Logical true or false value | "boolean" |
false |
Boolean |
| null | Intentional absence of value (assignment) | "object" BUG |
N/A | None |
| undefined | Variable declared but not assigned | "undefined" |
undefined |
None |
| symbol ES6 | Unique, immutable identifier for object properties | "symbol" |
N/A | Symbol |
| bigint ES2020 | Arbitrary precision integers (beyond 253-1) | "bigint" |
0n |
BigInt |
| Type | Literal Syntax | Constructor | Special Values |
|---|---|---|---|
| string | 'text', "text", `template` |
String("value") |
Empty string "" |
| number | 42, 3.14, 1e5, 0xFF, 0o77, 0b1010 |
Number("123") |
NaN, Infinity, -Infinity |
| boolean | true, false |
Boolean(value) |
Only true and false |
| null | null |
N/A | Only null |
| undefined | undefined |
undefined or void 0 |
Only undefined |
| symbol | N/A (must use function) | Symbol("desc") |
Each symbol is unique |
| bigint | 42n, 0xFFn, 0o77n, 0b1010n |
BigInt("123") |
No decimal values |
Example: Primitive type characteristics
// Immutability: primitives are immutable
let str = "hello";
str[0] = "H"; // No effect, strings are immutable
console.log(str); // "hello"
// Primitive vs Wrapper auto-boxing
let num = 42;
console.log(num.toString()); // "42" - temporary wrapper created
// Special number values
console.log(1 / 0); // Infinity
console.log(-1 / 0); // -Infinity
console.log(0 / 0); // NaN
console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991
// Symbols are unique
const sym1 = Symbol("desc");
const sym2 = Symbol("desc");
console.log(sym1 === sym2); // false (each is unique)
// BigInt for large integers
const bigNum = 9007199254740991n + 1n;
console.log(bigNum); // 9007199254740992n
Warning:
typeof null returns "object" due to a legacy bug. Use
value === null for null checking.
2. Reference Types (object, array, function)
| Type | Description | typeof Result | Mutable | Stored By |
|---|---|---|---|---|
| Object | Collection of key-value pairs (properties) | "object" |
✓ Yes | Reference |
| Array | Ordered collection with numeric indices (subtype of Object) | "object" |
✓ Yes | Reference |
| Function | Callable object with executable code | "function" |
✓ Yes (properties) | Reference |
| Date | Represents date and time (milliseconds since epoch) | "object" |
✓ Yes | Reference |
| RegExp | Regular expression pattern matcher | "object" |
✓ Yes | Reference |
| Map ES6 | Key-value pairs with any key type | "object" |
✓ Yes | Reference |
| Set ES6 | Collection of unique values | "object" |
✓ Yes | Reference |
| Behavior | Primitives | Reference Types |
|---|---|---|
| Assignment | Copies the value | Copies the reference (both point to same object) |
| Comparison (===) | Compares actual values | Compares references (same object?) |
| Mutability | Immutable (cannot change) | Mutable (can modify properties) |
| Storage | Stored directly in variable (stack) | Variable holds reference pointer (heap) |
| Passing to Functions | Pass by value (copy) | Pass by reference (can mutate original) |
Example: Primitive vs Reference behavior
// Primitives: value copied
let a = 5;
let b = a; // Copy value
b = 10;
console.log(a); // 5 (unchanged)
// References: pointer copied
let obj1 = { x: 5 };
let obj2 = obj1; // Copy reference
obj2.x = 10;
console.log(obj1.x); // 10 (both point to same object)
// Comparison
let str1 = "hello";
let str2 = "hello";
console.log(str1 === str2); // true (value comparison)
let arr1 = [1, 2, 3];
let arr2 = [1, 2, 3];
console.log(arr1 === arr2); // false (different references)
console.log(arr1 === arr1); // true (same reference)
// Function parameter behavior
function modifyPrimitive(x) {
x = 100; // Local copy modified
}
let num = 5;
modifyPrimitive(num);
console.log(num); // 5 (original unchanged)
function modifyObject(obj) {
obj.value = 100; // Original object modified
}
let data = { value: 5 };
modifyObject(data);
console.log(data.value); // 100 (original changed)
3. Type Conversion and Coercion Rules
| Target Type | From String | From Number | From Boolean | From Object |
|---|---|---|---|---|
| String | Unchanged | "5" |
"true"/"false" |
toString() or valueOf() |
| Number | 123 or NaN |
Unchanged | 1 / 0 |
valueOf() then toString() |
| Boolean | "" → false, else true |
0, NaN → false, else true |
Unchanged | Always true |
| Conversion Method | Syntax | Use Case | Example |
|---|---|---|---|
| Explicit String | String(x), x.toString(), x + "" |
Force string conversion | String(123) → "123" |
| Explicit Number | Number(x), +x, parseInt(), parseFloat() |
Force numeric conversion | Number("123") → 123 |
| Explicit Boolean | Boolean(x), !!x |
Force boolean conversion | Boolean(0) → false |
| Implicit Coercion | Operators: +, -, *, /, == |
Automatic conversion by operators | "5" * 2 → 10 |
| Value | to String | to Number | to Boolean |
|---|---|---|---|
| undefined | "undefined" |
NaN |
false |
| null | "null" |
0 |
false |
| true | "true" |
1 |
true |
| false | "false" |
0 |
false |
| "" | "" |
0 |
false |
| "123" | "123" |
123 |
true |
| "abc" | "abc" |
NaN |
true |
| 0, -0 | "0" |
0 |
false |
| NaN | "NaN" |
NaN |
false |
| Infinity | "Infinity" |
Infinity |
true |
| {}, [] | "[object Object]", "" |
NaN, 0 |
true |
Example: Type coercion in action
// String coercion (+ with string)
console.log("5" + 3); // "53" (number to string)
console.log("5" + true); // "5true"
// Numeric coercion (other operators)
console.log("5" - 3); // 2 (string to number)
console.log("5" * "2"); // 10
console.log(true + 1); // 2 (true → 1)
console.log(false + 1); // 1 (false → 0)
// Boolean coercion
console.log(Boolean("")); // false
console.log(Boolean(0)); // false
console.log(Boolean(NaN)); // false
console.log(Boolean(null)); // false
console.log(Boolean(undefined)); // false
console.log(Boolean([])); // true (object)
console.log(Boolean({})); // true (object)
// Explicit conversions
console.log(Number("123")); // 123
console.log(Number("12.5")); // 12.5
console.log(Number("abc")); // NaN
console.log(parseInt("123px")); // 123 (parses until non-digit)
console.log(String(123)); // "123"
console.log(+"42"); // 42 (unary +)
// Falsy values (6 total)
if (!undefined || !null || !0 || !NaN || !"" || !false) {
console.log("All falsy values");
}
Falsy Values (6 total):
false, 0, "", null,
undefined, NaN. Everything else is truthy (including [], {},
"0", "false").
4. Type Checking (typeof, instanceof, Array.isArray)
| Operator/Method | Syntax | Returns | Use Case | Limitations |
|---|---|---|---|---|
| typeof | typeof value |
String type name | Check primitive types | typeof null === "object" (bug); arrays return "object" |
| instanceof | obj instanceof Constructor |
Boolean | Check prototype chain | Doesn't work across frames/windows; primitives always false |
| Array.isArray() | Array.isArray(value) |
Boolean | Reliable array detection | Only for arrays |
| Object.prototype.toString | Object.prototype.toString.call(value) |
"[object Type]" |
Most reliable type checking | Verbose syntax |
| constructor | value.constructor === Type |
Boolean | Check constructor | Can be overridden; fails for null/undefined |
| Value | typeof | instanceof | Best Detection Method |
|---|---|---|---|
42 |
"number" |
N/A (primitive) | typeof x === "number" |
"text" |
"string" |
N/A (primitive) | typeof x === "string" |
true |
"boolean" |
N/A (primitive) | typeof x === "boolean" |
null |
"object" ⚠️ |
false | x === null |
undefined |
"undefined" |
false | x === undefined or typeof x === "undefined" |
[] |
"object" |
true (Array) | Array.isArray(x) |
{} |
"object" |
true (Object) | typeof x === "object" && x !== null |
function(){} |
"function" |
true (Function) | typeof x === "function" |
Symbol() |
"symbol" |
N/A (primitive) | typeof x === "symbol" |
42n |
"bigint" |
N/A (primitive) | typeof x === "bigint" |
Example: Type checking techniques
// typeof: basic primitive checking
console.log(typeof 42); // "number"
console.log(typeof "hello"); // "string"
console.log(typeof true); // "boolean"
console.log(typeof undefined); // "undefined"
console.log(typeof Symbol()); // "symbol"
console.log(typeof 42n); // "bigint"
console.log(typeof null); // "object" ⚠️ (legacy bug)
console.log(typeof []); // "object"
console.log(typeof {}); // "object"
console.log(typeof function(){}); // "function"
// instanceof: prototype chain checking
console.log([] instanceof Array); // true
console.log({} instanceof Object); // true
console.log(new Date() instanceof Date); // true
console.log(42 instanceof Number); // false (primitive)
// Array.isArray: reliable array detection
console.log(Array.isArray([])); // true
console.log(Array.isArray({})); // false
console.log(Array.isArray("array")); // false
// Object.prototype.toString: most reliable
const toString = Object.prototype.toString;
console.log(toString.call([])); // "[object Array]"
console.log(toString.call({})); // "[object Object]"
console.log(toString.call(null)); // "[object Null]"
console.log(toString.call(undefined)); // "[object Undefined]"
console.log(toString.call(new Date())); // "[object Date]"
console.log(toString.call(/regex/)); // "[object RegExp]"
// Utility type checking function
function getType(value) {
if (value === null) return 'null';
if (Array.isArray(value)) return 'array';
return typeof value;
}
console.log(getType(null)); // "null"
console.log(getType([])); // "array"
console.log(getType(42)); // "number"
5. Symbol Type and Well-known Symbols
| Feature | Description | Syntax | Use Case |
|---|---|---|---|
| Symbol Creation | Creates unique identifier | Symbol("description") |
Unique object property keys |
| Symbol Registry | Global shared symbols | Symbol.for("key") |
Cross-realm symbol sharing |
| Symbol.keyFor() | Get key from registry symbol | Symbol.keyFor(sym) |
Retrieve symbol key name |
| Symbol Properties | Hidden from enumeration | obj[sym] = value |
Private-like properties (not truly private) |
| Well-known Symbol | Purpose | Usage |
|---|---|---|
| Symbol.iterator | Defines default iterator | Used by for...of loops, spread operator |
| Symbol.asyncIterator | Defines async iterator | Used by for await...of loops |
| Symbol.toStringTag | Customizes toString() output |
Changes Object.prototype.toString.call() result |
| Symbol.toPrimitive | Defines type coercion behavior | Controls conversion to primitive types |
| Symbol.hasInstance | Customizes instanceof |
Overrides instanceof behavior |
| Symbol.species | Constructor for derived objects | Used in Array/Promise methods |
| Symbol.match, matchAll | String matching behavior | Used by String.prototype.match() |
| Symbol.replace, search, split | String manipulation behavior | Used by respective string methods |
Example: Symbol usage and well-known symbols
// Basic symbol creation (each is unique)
const sym1 = Symbol("id");
const sym2 = Symbol("id");
console.log(sym1 === sym2); // false (unique)
// Symbols as object properties
const id = Symbol("id");
const user = {
name: "John",
[id]: 123 // Symbol as computed property
};
console.log(user[id]); // 123
console.log(Object.keys(user)); // ["name"] (symbol hidden)
// Symbol registry (shared across realm)
const globalSym1 = Symbol.for("app.id");
const globalSym2 = Symbol.for("app.id");
console.log(globalSym1 === globalSym2); // true (same symbol)
console.log(Symbol.keyFor(globalSym1)); // "app.id"
// Well-known symbols: Symbol.iterator
const iterable = {
data: [1, 2, 3],
[Symbol.iterator]() {
let index = 0;
return {
next: () => ({
value: this.data[index],
done: index++ >= this.data.length
})
};
}
};
console.log([...iterable]); // [1, 2, 3]
// Symbol.toStringTag
class CustomClass {
get [Symbol.toStringTag]() {
return "CustomClass";
}
}
const obj = new CustomClass();
console.log(Object.prototype.toString.call(obj)); // "[object CustomClass]"
// Symbol.toPrimitive
const obj2 = {
[Symbol.toPrimitive](hint) {
if (hint === "number") return 42;
if (hint === "string") return "hello";
return true;
}
};
console.log(+obj2); // 42 (number hint)
console.log(`${obj2}`); // "hello" (string hint)
console.log(obj2 + ""); // "true" (default hint)
6. BigInt for Large Integer Operations
| Feature | Syntax | Description | Example |
|---|---|---|---|
| BigInt Literal | value + "n" |
Append 'n' to integer literal | 123n, 0xFFn, 0o77n, 0b1010n |
| BigInt Constructor | BigInt(value) |
Convert number/string to BigInt | BigInt("9007199254740991") |
| Range | Arbitrary precision | No upper/lower limit (memory-limited) | Can represent integers beyond 253-1 |
| Type | typeof x |
Returns "bigint" |
typeof 42n === "bigint" |
| Operation | BigInt Support | Example | Notes |
|---|---|---|---|
| Arithmetic | +, -, *, /, %, ** |
10n + 20n = 30n |
Both operands must be BigInt |
| Comparison | <, >, <=, >=, ==, != |
10n < 20n, 10n == 10 |
== coerces; === is strict |
| Bitwise | &, |, ^, ~, <<, >> |
5n & 3n = 1n |
>>> not supported (unsigned) |
| Division | / (integer division) |
7n / 2n = 3n |
Truncates decimal (rounds toward zero) |
| Unary Minus | -x |
-42n = -42n |
Negation supported |
| Unary Plus | ❌ Not supported | +42n ❌ TypeError |
Use Number() to convert |
| Limitation | Description | Workaround |
|---|---|---|
| No Mixed Operations | Cannot mix BigInt with Number | Explicitly convert: 10n + BigInt(5) or Number(10n) + 5 |
| No Decimal Values | BigInt only for integers | Use Number for decimal/floating-point |
| Math Object | Math.* methods don't support BigInt |
Implement custom logic or convert to Number |
| JSON Serialization | JSON.stringify() throws TypeError |
Custom replacer: JSON.stringify(obj, (k,v) => typeof v === 'bigint' ? v.toString() : v)
|
| Implicit Coercion | No automatic type coercion | Always explicit conversion required |
Example: BigInt operations and limitations
// Creating BigInts
const big1 = 9007199254740991n;
const big2 = BigInt("9007199254740991");
const big3 = BigInt(Number.MAX_SAFE_INTEGER);
// Arithmetic operations
console.log(100n + 50n); // 150n
console.log(100n - 50n); // 50n
console.log(100n * 2n); // 200n
console.log(100n / 3n); // 33n (integer division, truncates)
console.log(100n % 3n); // 1n
console.log(2n ** 100n); // Very large number (2^100)
// Comparison
console.log(10n === 10); // false (strict equality)
console.log(10n == 10); // true (loose equality with coercion)
console.log(10n < 20n); // true
console.log(5n > 3); // true (coercion in comparison)
// Limitations
// console.log(10n + 5); // ❌ TypeError: Cannot mix BigInt and Number
console.log(10n + BigInt(5)); // ✓ 15n (explicit conversion)
console.log(Number(10n) + 5); // ✓ 15 (convert to Number)
// console.log(+10n); // ❌ TypeError: Cannot convert BigInt
console.log(Number(10n)); // ✓ 10 (explicit conversion)
// Bitwise operations
console.log(5n & 3n); // 1n (binary AND)
console.log(5n | 3n); // 7n (binary OR)
console.log(5n ^ 3n); // 6n (binary XOR)
console.log(5n << 2n); // 20n (left shift)
// Use case: Precise large integer calculations
const largeNumber = 9007199254740992n; // Beyond Number.MAX_SAFE_INTEGER
console.log(largeNumber + 1n); // 9007199254740993n (exact)
console.log(Number.MAX_SAFE_INTEGER + 1); // 9007199254740992 (precision lost)
Warning: BigInt cannot be mixed with regular numbers in arithmetic operations. Always
explicitly convert using
BigInt() or Number().
Section 2 Summary
- JavaScript has 7 primitive types: string, number, boolean, null, undefined, symbol, bigint
- Primitives are immutable and passed by value; reference types are mutable and passed by reference
- Type coercion happens implicitly with operators; 6 falsy values exist:
false, 0, "", null, undefined, NaN - Use
typeoffor primitives,Array.isArray()for arrays, andinstanceoffor prototype checking - Symbols provide unique identifiers for object properties; well-known symbols customize language behavior
- BigInt enables arbitrary-precision integer math beyond Number.MAX_SAFE_INTEGER (253-1)