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)