2.3-Type-Mechanics
2.3 Type Mechanics
📚 Table of Contents
- •Type Coercion
- •Explicit Type Casting
- •typeof Operator
- •instanceof Operator
- •Truthy and Falsy Values
- •Equality Comparisons
Type Coercion
Type coercion is JavaScript's automatic (implicit) conversion of values from one type to another. This happens when operators or functions expect a certain type.
String Coercion
When JavaScript needs a string, it converts values automatically:
// String concatenation with + triggers string coercion
console.log('5' + 3); // "53" (3 → "3")
console.log('Hello' + true); // "Hellotrue"
console.log('Value: ' + null); // "Value: null"
console.log('Age: ' + undefined); // "Age: undefined"
console.log('' + 42); // "42"
// How different types become strings:
String(123); // "123"
String(true); // "true"
String(false); // "false"
String(null); // "null"
String(undefined); // "undefined"
String([1, 2, 3]); // "1,2,3"
String({ a: 1 }); // "[object Object]"
String(Symbol('x')); // "Symbol(x)"
Number Coercion
When JavaScript needs a number, it converts values:
// Arithmetic operators (except +) trigger number coercion
console.log('5' - 3); // 2 ("5" → 5)
console.log('6' * 2); // 12
console.log('10' / 2); // 5
console.log('5' % 2); // 1
console.log(+'42'); // 42 (unary + converts to number)
console.log(-'42'); // -42
// How different types become numbers:
Number('123'); // 123
Number('123.45'); // 123.45
Number('123abc'); // NaN (invalid)
Number(''); // 0
Number(' '); // 0
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN
Number([]); // 0 (empty array)
Number([5]); // 5 (single element)
Number([1, 2]); // NaN (multiple elements)
Number({}); // NaN
Boolean Coercion
When JavaScript needs a boolean (logical contexts):
// Logical operators trigger boolean coercion
if ('hello') {
} // "hello" → true
if (0) {
} // 0 → false
while (1) {} // 1 → true (infinite loop!)
// Logical NOT forces boolean coercion
console.log(!'hello'); // false
console.log(!0); // true
console.log(!null); // true
// Double NOT gives boolean equivalent
console.log(!!'hello'); // true
console.log(!!0); // false
console.log(!!null); // false
Coercion with Operators
// Addition (+) - prefers strings
console.log(1 + '2'); // "12" (1 → "1")
console.log('1' + 2); // "12" (2 → "2")
console.log(1 + 2 + '3'); // "33" (1+2=3, then "3")
console.log('1' + 2 + 3); // "123" (left to right)
// Subtraction, multiplication, division - always numbers
console.log('5' - 2); // 3
console.log('5' * '2'); // 10
console.log('10' / '2'); // 5
// Comparison operators
console.log('10' > 5); // true (string → number)
console.log('10' > '5'); // false (string comparison: "1" < "5")
// Loose equality coerces types
console.log(5 == '5'); // true
console.log(0 == false); // true
console.log(null == undefined); // true
The + Operator Special Cases
// + with objects calls valueOf() then toString()
console.log([] + []); // "" (both become "")
console.log([] + {}); // "[object Object]"
console.log({} + []); // "[object Object]" or 0 (context-dependent)
// + with dates
let date = new Date();
console.log(date + ''); // "Tue Dec 03 2024..." (string)
console.log(+date); // 1701619200000 (milliseconds)
Coercion Rules Summary Table
| Original | To String | To Number | To Boolean |
|---|---|---|---|
undefined | "undefined" | NaN | false |
null | "null" | 0 | false |
true | "true" | 1 | true |
false | "false" | 0 | false |
0 | "0" | 0 | false |
1 | "1" | 1 | true |
"" | "" | 0 | false |
"hello" | "hello" | NaN | true |
"42" | "42" | 42 | true |
[] | "" | 0 | true |
[1] | "1" | 1 | true |
[1,2] | "1,2" | NaN | true |
{} | "[object Object]" | NaN | true |
Explicit Type Casting
Explicit casting (also called type conversion) is when you intentionally convert a value to a specific type.
Converting to String
// Method 1: String() function
String(123); // "123"
String(true); // "true"
String(null); // "null"
String(undefined); // "undefined"
String([1, 2, 3]); // "1,2,3"
String({ a: 1 }); // "[object Object]"
// Method 2: .toString() method
(123).toString(); // "123"
true.toString(); // "true"
[1, 2, 3].toString(); // "1,2,3"
// null.toString(); // TypeError!
// undefined.toString(); // TypeError!
// Method 3: Template literal
`${123}`; // "123"
`${null}`; // "null"
// Method 4: Concatenation with empty string
123 + ''; // "123"
true + ''; // "true"
// Number to string with radix (base)
(255).toString(16); // "ff" (hexadecimal)
(255).toString(2); // "11111111" (binary)
(255).toString(8); // "377" (octal)
Converting to Number
// Method 1: Number() function
Number('42'); // 42
Number('3.14'); // 3.14
Number('42px'); // NaN (strict)
Number(''); // 0
Number(' '); // 0
Number(true); // 1
Number(false); // 0
Number(null); // 0
Number(undefined); // NaN
// Method 2: parseInt() - for integers
parseInt('42'); // 42
parseInt('42.9'); // 42 (truncates decimal)
parseInt('42px'); // 42 (parses until invalid char)
parseInt('px42'); // NaN (starts with invalid)
parseInt('0xFF'); // 255 (hex)
parseInt('1010', 2); // 10 (binary)
parseInt('FF', 16); // 255 (hex with radix)
// Method 3: parseFloat() - for decimals
parseFloat('3.14'); // 3.14
parseFloat('3.14.159'); // 3.14 (stops at second dot)
parseFloat('3.14px'); // 3.14
parseFloat('px3.14'); // NaN
// Method 4: Unary plus operator
+'42'; // 42
+'3.14'; // 3.14
+true; // 1
+false; // 0
+null; // 0
+undefined; // NaN
+''; // 0
+'hello'; // NaN
// Method 5: Multiply by 1 or divide by 1
'42' * 1; // 42
'42' / 1; // 42
// Method 6: Bitwise operators (truncates to 32-bit integer)
'42.9' | 0; // 42
~~'42.9'; // 42
'42.9' >> 0; // 42
Converting to Boolean
// Method 1: Boolean() function
Boolean(1); // true
Boolean(0); // false
Boolean(''); // false
Boolean('hello'); // true
Boolean(null); // false
Boolean(undefined); // false
Boolean([]); // true (!)
Boolean({}); // true (!)
// Method 2: Double NOT operator
!!1; // true
!!0; // false
!!'hello'; // true
!!''; // false
!!null; // false
!![]; // true
// The 8 falsy values:
Boolean(false); // false
Boolean(0); // false
Boolean(-0); // false
Boolean(0n); // false (BigInt zero)
Boolean(''); // false
Boolean(null); // false
Boolean(undefined); // false
Boolean(NaN); // false
Type Conversion Best Practices
// ✅ Use explicit conversion for clarity
const userInput = '42';
const number = Number(userInput);
// ✅ Use appropriate method for the situation
const age = parseInt(userInput, 10); // Always specify radix!
const price = parseFloat('19.99');
// ✅ Check for NaN after conversion
const num = Number('hello');
if (Number.isNaN(num)) {
console.log('Invalid number');
}
// ❌ Don't rely on implicit coercion for important logic
// if (userInput) { } // "" is falsy but might be valid!
// ✅ Be explicit
if (userInput !== '') {
}
typeof Operator
The typeof operator returns a string indicating the type of a value.
typeof Results
// Primitive types
typeof undefined; // "undefined"
typeof null; // "object" (historical bug!)
typeof true; // "boolean"
typeof 42; // "number"
typeof 42n; // "bigint"
typeof 'hello'; // "string"
typeof Symbol(); // "symbol"
// Reference types
typeof {}; // "object"
typeof []; // "object" (arrays are objects!)
typeof function () {}; // "function"
typeof new Date(); // "object"
typeof /regex/; // "object"
typeof new Map(); // "object"
typeof new Set(); // "object"
// Special cases
typeof NaN; // "number" (NaN is a number!)
typeof Infinity; // "number"
typeof Math; // "object"
typeof JSON; // "object"
typeof Quirks and Gotchas
// The null bug
typeof null === 'object'; // true (famous bug!)
// Checking for null properly
let value = null;
value === null; // true
// Arrays are objects
typeof []; // "object"
Array.isArray([]); // true (correct way)
// Functions are callable objects
typeof function () {}; // "function"
typeof (() => {}); // "function"
typeof class {}; // "function" (classes are functions!)
// Undeclared variables
typeof undeclaredVar; // "undefined" (no error!)
// undeclaredVar; // ReferenceError!
Practical typeof Usage
// Type checking function
function checkType(value) {
if (value === null) {
return 'null';
}
if (Array.isArray(value)) {
return 'array';
}
return typeof value;
}
// Defensive programming
function processNumber(num) {
if (typeof num !== 'number' || Number.isNaN(num)) {
throw new TypeError('Expected a valid number');
}
return num * 2;
}
// Feature detection
if (typeof localStorage !== 'undefined') {
// localStorage is available
}
// Checking for undefined
let x;
if (typeof x === 'undefined') {
console.log('x is undefined');
}
instanceof Operator
The instanceof operator tests whether an object has a specific constructor's prototype in its prototype chain.
Basic Usage
// Arrays
let arr = [1, 2, 3];
arr instanceof Array; // true
arr instanceof Object; // true (arrays are objects)
// Functions
function greet() {}
greet instanceof Function; // true
greet instanceof Object; // true
// Objects
let obj = {};
obj instanceof Object; // true
obj instanceof Array; // false
// Custom classes
class Person {
constructor(name) {
this.name = name;
}
}
class Employee extends Person {
constructor(name, job) {
super(name);
this.job = job;
}
}
let emp = new Employee('John', 'Developer');
emp instanceof Employee; // true
emp instanceof Person; // true (inheritance)
emp instanceof Object; // true
emp instanceof Array; // false
instanceof with Built-in Types
// Dates
new Date() instanceof Date; // true
new Date() instanceof Object; // true
// Regular expressions
/hello/ instanceof RegExp; // true
// Errors
new Error() instanceof Error; // true
new TypeError() instanceof Error; // true
// Maps and Sets
new Map() instanceof Map; // true
new Set() instanceof Set; // true
// Primitives - instanceof doesn't work!
'hello' instanceof String; // false
42 instanceof Number; // false
true instanceof Boolean; // false
// Wrapper objects work
new String('hello') instanceof String; // true
new Number(42) instanceof Number; // true
instanceof Limitations
// Primitives don't work
'hello' instanceof String; // false
42 instanceof Number; // false
// Cross-frame issues (in browsers)
// Arrays created in one iframe may not be instanceof Array in another
// Safer alternative for arrays
Array.isArray([]); // true (works across frames)
// Symbol.hasInstance can be customized
class MyArray {
static [Symbol.hasInstance](instance) {
return Array.isArray(instance);
}
}
[] instanceof MyArray; // true (custom behavior)
typeof vs instanceof
| Feature | typeof | instanceof |
|---|---|---|
| Works with primitives | Yes | No |
| Works with objects | Yes | Yes |
| Returns | String | Boolean |
| Checks prototype chain | No | Yes |
| Works across frames | Yes | No (for objects) |
// Use typeof for primitives
typeof 'hello' === 'string'; // true
// Use instanceof for class instances
new Date() instanceof Date; // true
// Combine for comprehensive checking
function isString(value) {
return typeof value === 'string' || value instanceof String;
}
Truthy and Falsy Values
JavaScript converts values to booleans in logical contexts. Understanding truthy/falsy is crucial.
The 8 Falsy Values
// These are the ONLY falsy values in JavaScript:
Boolean(false); // false - the boolean false
Boolean(0); // false - zero
Boolean(-0); // false - negative zero
Boolean(0n); // false - BigInt zero
Boolean(''); // false - empty string
Boolean(null); // false - null
Boolean(undefined); // false - undefined
Boolean(NaN); // false - Not a Number
// Everything else is truthy!
Truthy Values (Common Examples)
// All of these are truthy:
Boolean(true); // true
Boolean(1); // true
Boolean(-1); // true (any non-zero number)
Boolean(3.14); // true
Boolean('hello'); // true
Boolean('0'); // true (non-empty string!)
Boolean('false'); // true (non-empty string!)
Boolean([]); // true (empty array!)
Boolean({}); // true (empty object!)
Boolean(function () {}); // true
Boolean(Infinity); // true
Boolean(-Infinity); // true
Boolean(new Date()); // true
Boolean(Symbol()); // true
Boolean(1n); // true (non-zero BigInt)
Common Truthy/Falsy Mistakes
// Empty array is truthy!
if ([]) {
console.log('This runs!'); // ✅ Runs
}
// But empty array equals false?!
console.log([] == false); // true (coercion)
console.log(!![]); // true (it's truthy)
// Check array length instead
if ([].length > 0) {
console.log('Array has items');
}
// "0" is truthy (non-empty string)
if ('0') {
console.log('This runs!'); // ✅ Runs
}
// Empty object is truthy
if ({}) {
console.log('This runs!'); // ✅ Runs
}
// Check for empty object
if (Object.keys({}).length > 0) {
console.log('Object has properties');
}
Truthy/Falsy in Practice
// Default values with OR (||)
function greet(name) {
name = name || 'Guest'; // Uses "Guest" if name is falsy
return `Hello, ${name}!`;
}
greet('John'); // "Hello, John!"
greet(''); // "Hello, Guest!" (empty string is falsy)
greet(0); // "Hello, Guest!" (0 is falsy - might be wrong!)
// Better: Nullish coalescing (??)
function greetBetter(name) {
name = name ?? 'Guest'; // Only uses "Guest" if null/undefined
return `Hello, ${name}!`;
}
greetBetter(''); // "Hello, !" (empty string is kept)
greetBetter(0); // "Hello, 0!" (0 is kept)
greetBetter(null); // "Hello, Guest!"
// Short-circuit evaluation
const user = {
name: 'John',
getAddress: function () {
return this.address && this.address.city; // Returns undefined if no address
},
};
// Modern alternative: optional chaining
const city = user.address?.city;
Equality Comparisons
JavaScript has two types of equality: loose (==) and strict (===).
Strict Equality (===)
No type coercion - values must be the same type AND same value.
// Same type, same value
5 === 5; // true
"hello" === "hello"; // true
true === true; // true
// Different types
5 === "5"; // false (different types)
0 === false; // false (different types)
null === undefined; // false (different types)
// Special cases
NaN === NaN; // false (NaN is never equal to anything!)
+0 === -0; // true (considered equal)
// Objects (reference comparison)
{} === {}; // false (different objects)
let a = {};
let b = a;
a === b; // true (same reference)
Loose Equality (==)
Type coercion happens - JavaScript converts types before comparing.
// String to Number
5 == '5'; // true ("5" → 5)
0 == ''; // true ("" → 0)
42 == '42'; // true
// Boolean to Number
true == 1; // true (true → 1)
false == 0; // true (false → 0)
true == '1'; // true (true → 1, "1" → 1)
// null and undefined
null == undefined; // true (special rule)
null == 0; // false (null only equals undefined)
undefined == 0; // false
// Objects to primitives
[1] == 1; // true ([1] → "1" → 1)
[1, 2] == '1,2'; // true ([1,2] → "1,2")
The Abstract Equality Algorithm (==)
When comparing x == y:
- •If same type, compare directly
- •If
nullandundefined, returntrue - •If number and string, convert string to number
- •If boolean, convert boolean to number
- •If object and primitive, convert object to primitive
// Example walkthrough:
// "5" == 5
// Step 1: Different types (string vs number)
// Step 3: Convert "5" to 5
// Result: 5 == 5 → true
// true == "1"
// Step 1: Different types (boolean vs string)
// Step 4: Convert true to 1
// Now: 1 == "1"
// Step 3: Convert "1" to 1
// Result: 1 == 1 → true
Comparison Table
| Expression | == | === |
|---|---|---|
5 == "5" | ✅ true | ❌ false |
0 == false | ✅ true | ❌ false |
null == undefined | ✅ true | ❌ false |
NaN == NaN | ❌ false | ❌ false |
[] == false | ✅ true | ❌ false |
[] == 0 | ✅ true | ❌ false |
"" == 0 | ✅ true | ❌ false |
"" == false | ✅ true | ❌ false |
Object.is() - The Strictest Comparison
// Object.is() is like === but handles edge cases differently
Object.is(5, 5); // true
Object.is(5, '5'); // false (no coercion)
Object.is(NaN, NaN); // true (unlike ===!)
Object.is(+0, -0); // false (unlike ===!)
Object.is(null, undefined); // false
Best Practices
// ✅ Always use strict equality (===)
if (value === 5) {
}
if (name === 'John') {
}
// ✅ Use Object.is() for NaN comparisons
if (Object.is(value, NaN)) {
}
// Or use Number.isNaN()
if (Number.isNaN(value)) {
}
// ✅ Exception: checking for null/undefined
if (value == null) {
// Matches both null and undefined
}
// Equivalent to:
if (value === null || value === undefined) {
}
// ❌ Avoid loose equality
if (value == 5) {
} // Could match "5", true, etc.
🎯 Key Takeaways
- •Type coercion is automatic type conversion - understand it but don't rely on it
- •Explicit casting with
String(),Number(),Boolean()is clearer - •typeof returns a string but has quirks (null → "object")
- •instanceof checks the prototype chain, works with objects
- •8 falsy values:
false,0,-0,0n,"",null,undefined,NaN - •Always use
===for comparisons unless you specifically need coercion - •Use
Object.is()orNumber.isNaN()for NaN comparisons
➡️ Next Module
Continue to Module 3: Operators to learn about all JavaScript operators!