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 typeof for primitives, Array.isArray() for arrays, and instanceof for 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)