Docs

2.3-Type-Mechanics

2.3 Type Mechanics

📚 Table of Contents

  1. Type Coercion
  2. Explicit Type Casting
  3. typeof Operator
  4. instanceof Operator
  5. Truthy and Falsy Values
  6. 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

OriginalTo StringTo NumberTo Boolean
undefined"undefined"NaNfalse
null"null"0false
true"true"1true
false"false"0false
0"0"0false
1"1"1true
""""0false
"hello""hello"NaNtrue
"42""42"42true
[]""0true
[1]"1"1true
[1,2]"1,2"NaNtrue
{}"[object Object]"NaNtrue

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

Featuretypeofinstanceof
Works with primitivesYesNo
Works with objectsYesYes
ReturnsStringBoolean
Checks prototype chainNoYes
Works across framesYesNo (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:

  1. If same type, compare directly
  2. If null and undefined, return true
  3. If number and string, convert string to number
  4. If boolean, convert boolean to number
  5. 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

  1. Type coercion is automatic type conversion - understand it but don't rely on it
  2. Explicit casting with String(), Number(), Boolean() is clearer
  3. typeof returns a string but has quirks (null → "object")
  4. instanceof checks the prototype chain, works with objects
  5. 8 falsy values: false, 0, -0, 0n, "", null, undefined, NaN
  6. Always use === for comparisons unless you specifically need coercion
  7. Use Object.is() or Number.isNaN() for NaN comparisons

➡️ Next Module

Continue to Module 3: Operators to learn about all JavaScript operators!

.3 Type Mechanics - JavaScript Tutorial | DeepML