JavaScript Tutorial

JavaScript Type Conversion & Coercion: Complete Guide

Master JavaScript type conversion and coercion. Learn the difference between explicit conversion and implicit coercion, understand truthy/falsy values, and avoid common pitfalls.

Welcome back! 👋 In the previous lesson, you learned about JavaScript's data types. Now, let's dive deeper into one of JavaScript's most important (and sometimes confusing) features: Type Conversion and Type Coercion.

Understanding how JavaScript converts between types is crucial for writing bug-free code and avoiding unexpected behavior. Let's master this essential concept!


What is Type Conversion?

Type conversion (also called type casting) is the process of converting a value from one data type to another.

JavaScript has two types of conversion:

1. Explicit Conversion (Type Casting)

When you manually convert a value from one type to another.

let str = "123";
let num = Number(str);  // YOU explicitly convert string to number
console.log(num);       // 123
console.log(typeof num); // "number"

2. Implicit Conversion (Type Coercion)

When JavaScript automatically converts a value from one type to another.

let result = "5" + 3;   // JavaScript automatically converts 3 to "3"
console.log(result);    // "53" (string)

let result2 = "5" - 3;  // JavaScript automatically converts "5" to 5
console.log(result2);   // 2 (number)

Why Does Type Conversion Matter?

JavaScript is dynamically typed and weakly typed, meaning it tries to be helpful by automatically converting types. This can lead to:

Good things:

  • More flexible code
  • Less verbose syntax
  • Quick prototyping

Bad things:

  • Unexpected behavior
  • Hard-to-find bugs
  • Confusion for beginners

Example of potential confusion:

console.log("5" + 3);   // "53" (string concatenation)
console.log("5" - 3);   // 2 (numeric subtraction)
console.log("5" * 3);   // 15 (numeric multiplication)

// Why different behavior? Type coercion rules!

Explicit Type Conversion (Type Casting)

Let's learn how to manually convert between types.


Converting to String

Method 1: String() Function (Recommended)

// Numbers to strings
console.log(String(123));        // "123"
console.log(String(3.14));       // "3.14"
console.log(String(-42));        // "-42"

// Booleans to strings
console.log(String(true));       // "true"
console.log(String(false));      // "false"

// null and undefined to strings
console.log(String(null));       // "null"
console.log(String(undefined));  // "undefined"

// Objects and arrays
console.log(String([1, 2, 3]));  // "1,2,3"
console.log(String({a: 1}));     // "[object Object]"

Method 2: .toString() Method

let num = 123;
console.log(num.toString());     // "123"

let bool = true;
console.log(bool.toString());    // "true"

let arr = [1, 2, 3];
console.log(arr.toString());     // "1,2,3"

// ⚠️ Cannot use on null or undefined
// null.toString();              // ❌ TypeError
// undefined.toString();         // ❌ TypeError

Method 3: Template Literals (Implicit but Explicit)

let num = 123;
console.log(`${num}`);           // "123"

let bool = true;
console.log(`${bool}`);          // "true"

// Very readable for string interpolation
let age = 30;
console.log(`I am ${age} years old`); // "I am 30 years old"

Method 4: Concatenation with Empty String

let num = 123;
console.log(num + "");           // "123"

let bool = true;
console.log(bool + "");          // "true"

// Simple but less explicit

When to Use Each Method:

  • String() - Most explicit and safe (works with null/undefined)
  • .toString() - When you know value is not null/undefined
  • Template literals - When building strings with variables
  • + "" - Quick conversion (but less clear)

Converting to Number

Method 1: Number() Function (Recommended)

// Strings to numbers
console.log(Number("123"));      // 123
console.log(Number("3.14"));     // 3.14
console.log(Number("   42  "));  // 42 (trims whitespace)

// Booleans to numbers
console.log(Number(true));       // 1
console.log(Number(false));      // 0

// null and undefined to numbers
console.log(Number(null));       // 0
console.log(Number(undefined));  // NaN

// Invalid conversions
console.log(Number("abc"));      // NaN
console.log(Number("12abc"));    // NaN
console.log(Number(""));         // 0 (empty string becomes 0!)

Method 2: parseInt() and parseFloat()

// parseInt() - Converts to integer
console.log(parseInt("123"));        // 123
console.log(parseInt("123.45"));     // 123 (removes decimal)
console.log(parseInt("123abc"));     // 123 (stops at non-digit)
console.log(parseInt("abc123"));     // NaN (starts with non-digit)
console.log(parseInt("   42  "));    // 42 (trims whitespace)

// parseFloat() - Converts to decimal
console.log(parseFloat("3.14"));     // 3.14
console.log(parseFloat("3.14abc"));  // 3.14 (stops at non-digit)
console.log(parseFloat("123"));      // 123

// parseInt() with radix (base)
console.log(parseInt("10", 10));     // 10 (decimal)
console.log(parseInt("10", 2));      // 2 (binary)
console.log(parseInt("10", 16));     // 16 (hexadecimal)
console.log(parseInt("FF", 16));     // 255 (hex to decimal)

// Always specify radix to avoid confusion!
console.log(parseInt("08"));         // 8 (but could be interpreted as octal in old JS)
console.log(parseInt("08", 10));     // 8 (explicitly decimal)

Method 3: Unary Plus Operator (+)

let str = "123";
console.log(+str);               // 123

console.log(+"3.14");            // 3.14
console.log(+true);              // 1
console.log(+false);             // 0
console.log(+null);              // 0
console.log(+undefined);         // NaN
console.log(+"");                // 0
console.log(+"abc");             // NaN

// Quick and concise but less explicit

Method 4: Mathematical Operations

let str = "123";
console.log(str * 1);            // 123
console.log(str - 0);            // 123
console.log(str / 1);            // 123

// Works but not recommended (unclear intent)

Comparison: Number() vs parseInt() vs parseFloat()

let value = "123.45abc";

console.log(Number(value));      // NaN (strict - fails on invalid)
console.log(parseInt(value));    // 123 (lenient - parses what it can)
console.log(parseFloat(value));  // 123.45 (lenient - parses what it can)

// Another example
let value2 = "  42  ";
console.log(Number(value2));     // 42 (handles whitespace)
console.log(parseInt(value2));   // 42 (handles whitespace)

When to Use Each Method:

  • Number() - When you want strict conversion (all or nothing)
  • parseInt() - When extracting integers from strings
  • parseFloat() - When extracting decimals from strings
  • Unary + - Quick conversion when you know format is valid

Converting to Boolean

Method 1: Boolean() Function (Recommended)

// Falsy values (convert to false)
console.log(Boolean(false));     // false
console.log(Boolean(0));         // false
console.log(Boolean(-0));        // false
console.log(Boolean(""));        // false
console.log(Boolean(null));      // false
console.log(Boolean(undefined)); // false
console.log(Boolean(NaN));       // false

// Truthy values (convert to true)
console.log(Boolean(true));      // true
console.log(Boolean(1));         // true
console.log(Boolean(-1));        // true
console.log(Boolean("hello"));   // true
console.log(Boolean("0"));       // true (string "0" is truthy!)
console.log(Boolean(" "));       // true (space is truthy!)
console.log(Boolean([]));        // true (empty array is truthy!)
console.log(Boolean({}));        // true (empty object is truthy!)
console.log(Boolean(function(){})); // true

Method 2: Double NOT Operator (!!)

console.log(!!1);                // true
console.log(!!0);                // false
console.log(!!"hello");          // true
console.log(!!"");               // false
console.log(!!null);             // false
console.log(!!undefined);        // false

// How it works:
// !value - converts to boolean and inverts
// !!value - converts to boolean and inverts twice (back to original truth value)

let value = "hello";
console.log(!value);             // false (inverted)
console.log(!!value);            // true (double inverted = original)

Method 3: Conditional Context (Implicit)

let value = "hello";

// Automatically converts to boolean in conditions
if (value) {
  console.log("Truthy!"); // Executes
}

// Ternary operator
let result = value ? "yes" : "no"; // "yes"

// Logical operators
console.log(value && "success");   // "success"
console.log(0 || "default");       // "default"

Complete Truthy/Falsy Reference:

Falsy Values (Only 7!):

false         // The boolean false
0             // Zero
-0            // Negative zero
0n            // BigInt zero
""            // Empty string
null          // Null value
undefined     // Undefined value
NaN           // Not a Number

Everything else is truthy, including:

true          // Boolean true
1, -1, 42     // Non-zero numbers
"0", "false"  // Non-empty strings (even "0" and "false"!)
" "           // String with just space
[]            // Empty array
{}            // Empty object
function(){}  // Functions
Infinity      // Infinity
new Date()    // Date objects

Implicit Type Coercion

Now let's explore how JavaScript automatically converts types in different operations.


String Coercion (+ Operator)

When + is used with a string, everything becomes a string:

// String + anything = String (concatenation)
console.log("Hello" + " World");    // "Hello World"
console.log("5" + 3);               // "53"
console.log("5" + true);            // "5true"
console.log("5" + null);            // "5null"
console.log("5" + undefined);       // "5undefined"
console.log("5" + [1, 2]);          // "51,2"
console.log("5" + {a: 1});          // "5[object Object]"

// Number + String = String (order matters!)
console.log(5 + "3");               // "53"
console.log(5 + 3 + "2");           // "82" (5+3=8, then "8"+"2"="82")
console.log("2" + 5 + 3);           // "253" ("2"+5="25", "25"+3="253")

// Multiple additions (left to right)
console.log(1 + 2 + "3");           // "33" (1+2=3, 3+"3"="33")
console.log("1" + 2 + 3);           // "123" ("1"+2="12", "12"+3="123")

Rule: If any operand is a string with +, the result is a string.


Numeric Coercion (-, *, /, %)

Other arithmetic operators convert to numbers:

// String - Number = Number (subtraction)
console.log("5" - 3);               // 2
console.log("10" - "3");            // 7
console.log("5" - true);            // 4 (true becomes 1)
console.log("5" - false);           // 5 (false becomes 0)
console.log("5" - null);            // 5 (null becomes 0)
console.log("5" - undefined);       // NaN (undefined becomes NaN)

// String * Number = Number (multiplication)
console.log("5" * 3);               // 15
console.log("10" * "2");            // 20
console.log("5" * true);            // 5 (true becomes 1)

// String / Number = Number (division)
console.log("10" / 2);              // 5
console.log("10" / "2");            // 5

// String % Number = Number (modulus)
console.log("10" % 3);              // 1
console.log("10" % "3");            // 1

// Invalid conversions result in NaN
console.log("abc" - 3);             // NaN
console.log("abc" * 2);             // NaN
console.log("5" - "abc");           // NaN

Rule: -, *, /, % always try to convert operands to numbers.


Boolean Coercion (Logical Context)

In logical contexts, values are coerced to boolean:

// if statements
if ("hello") {
  console.log("Truthy!"); // Executes
}

if (0) {
  console.log("Won't execute"); // Doesn't execute
}

// Logical NOT (!)
console.log(!1);                    // false (1 is truthy)
console.log(!0);                    // true (0 is falsy)
console.log(!"hello");              // false
console.log(!"");                   // true

// Logical AND (&&)
console.log(true && "result");      // "result" (returns last truthy)
console.log(false && "result");     // false (returns first falsy)
console.log(1 && 2 && 3);           // 3 (all truthy, returns last)
console.log(1 && 0 && 3);           // 0 (returns first falsy)

// Logical OR (||)
console.log(false || "default");    // "default" (returns first truthy)
console.log(true || "default");     // true (returns first truthy)
console.log(0 || null || "value");  // "value" (first truthy)

// Ternary operator
let age = 18;
let status = age >= 18 ? "adult" : "minor"; // "adult"

Short-circuit Evaluation:

// && returns first falsy or last truthy
console.log(0 && "never checked");  // 0 (stops at first falsy)
console.log(1 && 2 && 3);           // 3 (all truthy, returns last)

// || returns first truthy or last falsy
console.log(1 || "never checked");  // 1 (stops at first truthy)
console.log(0 || null || undefined); // undefined (all falsy, returns last)

// Practical use: Default values
let userName = "";
let displayName = userName || "Guest"; // "Guest" (userName is falsy)

let count = 0;
let display = count || 10;          // 10 (be careful with 0!)

Comparison Coercion

Loose Equality (==)

== performs type coercion before comparison:

// Number comparisons
console.log(5 == "5");              // true (string "5" converts to number 5)
console.log(0 == false);            // true (false converts to 0)
console.log(1 == true);             // true (true converts to 1)

// null and undefined are special
console.log(null == undefined);     // true (special rule)
console.log(null == 0);             // false (doesn't convert to 0)
console.log(undefined == 0);        // false

// Empty values
console.log("" == 0);               // true (empty string converts to 0)
console.log("0" == 0);              // true (string "0" converts to 0)
console.log(false == "");           // true (both convert to 0)

// Arrays and objects
console.log([] == 0);               // true (empty array converts to 0)
console.log([] == "");              // true (empty array converts to "")
console.log([1] == 1);              // true (array converts to "1", then to 1)

// Confusing cases!
console.log("0" == false);          // true (both convert to 0)
console.log("" == false);           // true (both convert to 0)
console.log(" " == false);          // false (" " is truthy but doesn't equal false!)

Strict Equality (===)

=== does NOT perform type coercion:

// Must be same type AND same value
console.log(5 === "5");             // false (different types)
console.log(0 === false);           // false (different types)
console.log(1 === true);            // false (different types)
console.log(null === undefined);    // false (different types)

// Same type and value
console.log(5 === 5);               // true
console.log("5" === "5");           // true
console.log(true === true);         // true

// Always use === unless you have a specific reason to use ==

Comparison Operator Coercion Rules:

// < > <= >= convert to numbers
console.log("10" > 5);              // true ("10" converts to 10)
console.log("2" > "12");            // true (string comparison! "2" > "1")
console.log("2" > 12);              // false (number comparison! 2 < 12)
console.log("abc" > 5);             // false (NaN comparisons are always false)

// String comparison is lexicographical (dictionary order)
console.log("apple" < "banana");    // true
console.log("10" < "9");            // true ("1" < "9" in string comparison)
console.log(10 < 9);                // false (numeric comparison)

Visual Summary:

==  (Loose Equality)         ===  (Strict Equality)
────────────────────         ─────────────────────
5 == "5"true          5 === "5"false
0 == falsetrue          0 === falsefalse
"" == 0true          "" === 0false
null == undefinedtrue     null === undefinedfalse

USE === ALMOST ALWAYS!       Use == only when specifically needed

Common Type Coercion Pitfalls

Pitfall 1: Addition vs Concatenation

// Problem: Confusing + operator
let a = "5";
let b = 3;
console.log(a + b);             // "53" (concatenation, not addition!)

// Solution: Explicit conversion
console.log(Number(a) + b);     // 8 (addition)
console.log(+a + b);            // 8 (addition)

Pitfall 2: Truthy vs True

// Problem: Truthy doesn't mean === true
console.log("0" == true);       // false
console.log(Boolean("0"));      // true (truthy)

console.log([] == true);        // false
console.log(Boolean([]));       // true (truthy)

// Solution: Use explicit boolean conversion or strict comparison
if (Boolean("0")) {             // ✅ Explicit
  console.log("String '0' is truthy");
}

if ("0") {                      // ✅ Also fine (implicit truthy check)
  console.log("String '0' is truthy");
}

Pitfall 3: Empty String Converts to 0

// Problem: Empty string is falsy but converts to 0
console.log(Number(""));        // 0
console.log("" == 0);           // true
console.log(Boolean(""));       // false

// This can cause issues
function calculateTotal(input) {
  return Number(input) + 10;
}

console.log(calculateTotal(""));     // 10 (intended?) 
console.log(calculateTotal("5"));    // 15

// Solution: Check for empty string explicitly
function calculateTotal(input) {
  if (input === "") {
    return 0;
  }
  return Number(input) + 10;
}

Pitfall 4: NaN Comparisons

// Problem: NaN is not equal to anything, including itself
console.log(NaN == NaN);        // false
console.log(NaN === NaN);       // false

let result = "abc" * 2;
console.log(result == NaN);     // false (can't check this way!)

// Solution: Use isNaN() or Number.isNaN()
console.log(isNaN(result));     // true
console.log(Number.isNaN(result)); // true (more reliable)

Pitfall 5: null vs undefined

// Problem: They're similar but different
console.log(null == undefined);  // true (loose equality)
console.log(null === undefined); // false (strict equality)

console.log(null == 0);         // false (doesn't convert)
console.log(null < 1);          // true (converts to 0)
console.log(null > -1);         // true (converts to 0)

// Solution: Always use === for null/undefined checks
if (value === null) {           // ✅ Explicit
  console.log("Value is null");
}

if (value === undefined) {      // ✅ Explicit
  console.log("Value is undefined");
}

// Or check for both
if (value == null) {            // ✅ Only case where == is useful
  console.log("Value is null or undefined");
}

Pitfall 6: Array to Primitive Conversion

// Problem: Arrays convert in surprising ways
console.log([1, 2, 3] + [4, 5, 6]); // "1,2,34,5,6" (string concatenation!)
console.log([] + []);                // "" (empty string)
console.log([] + {});                // "[object Object]"
console.log({} + []);                // "[object Object]" or 0 (depends on context!)

// Arrays convert to strings via .join(",")
console.log([1, 2, 3].toString());   // "1,2,3"
console.log(String([1, 2, 3]));      // "1,2,3"

// Solution: Be explicit about what you want
let arr1 = [1, 2, 3];
let arr2 = [4, 5, 6];
console.log([...arr1, ...arr2]);     // [1, 2, 3, 4, 5, 6] (concatenate arrays)

Type Conversion Best Practices

1. Always Use Strict Equality (===)

// ❌ Bad - unpredictable
if (value == 0) {
  console.log("Is zero?");
}

// ✅ Good - explicit
if (value === 0) {
  console.log("Is exactly zero");
}

// Exception: Checking for null/undefined
if (value == null) {  // ✅ OK - checks both null and undefined
  console.log("Is null or undefined");
}

2. Be Explicit with Type Conversions

// ❌ Bad - relying on coercion
let total = "5" + 3;  // "53"
let result = "5" - 3; // 2

// ✅ Good - explicit conversion
let total = Number("5") + 3;  // 8
let result = Number("5") - 3; // 2

// ✅ Good - clear intent
let str = String(123);
let num = Number("123");
let bool = Boolean(1);

3. Validate Input Types

// ❌ Bad - assuming type
function add(a, b) {
  return a + b;  // Could concatenate strings!
}

// ✅ Good - validate types
function add(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("Both arguments must be numbers");
  }
  return a + b;
}

// ✅ Good - convert and validate
function add(a, b) {
  const numA = Number(a);
  const numB = Number(b);
  
  if (isNaN(numA) || isNaN(numB)) {
    throw new TypeError("Cannot convert arguments to numbers");
  }
  
  return numA + numB;
}

4. Use Template Literals for String Building

// ❌ Bad - confusing with +
let message = "Total: " + price + " (" + quantity + " items)";

// ✅ Good - clear intent
let message = `Total: ${price} (${quantity} items)`;

5. Handle Edge Cases

// ❌ Bad - doesn't handle edge cases
function parseUserInput(input) {
  return Number(input);
}

console.log(parseUserInput(""));        // 0 (is this intended?)
console.log(parseUserInput("abc"));     // NaN (how to handle?)
console.log(parseUserInput(null));      // 0 (is this intended?)

// ✅ Good - explicit handling
function parseUserInput(input) {
  // Handle empty/null/undefined
  if (input === "" || input == null) {
    return null;  // or throw error, or return default
  }
  
  const num = Number(input);
  
  // Handle NaN
  if (isNaN(num)) {
    throw new Error(`Invalid number: ${input}`);
  }
  
  return num;
}

6. Use isNaN() Correctly

// ❌ Bad - isNaN coerces to number first
console.log(isNaN("abc"));      // true (expected)
console.log(isNaN("123"));      // false (expected)
console.log(isNaN("123abc"));   // true (expected)
console.log(isNaN({}));         // true (unexpected?)

// ✅ Better - Number.isNaN() doesn't coerce
console.log(Number.isNaN(NaN));     // true
console.log(Number.isNaN("abc"));   // false (not actually NaN)
console.log(Number.isNaN(123));     // false

// ✅ Best - validate type first
function isValidNumber(value) {
  return typeof value === "number" && !isNaN(value) && isFinite(value);
}

console.log(isValidNumber(123));      // true
console.log(isValidNumber("123"));    // false (string, not number)
console.log(isValidNumber(NaN));      // false
console.log(isValidNumber(Infinity)); // false

Practical Examples

Example 1: Form Input Validation

function validateAge(input) {
  // Handle empty input
  if (input === "" || input == null) {
    return { valid: false, error: "Age is required" };
  }
  
  // Convert to number
  const age = Number(input);
  
  // Check if valid number
  if (isNaN(age)) {
    return { valid: false, error: "Age must be a number" };
  }
  
  // Check range
  if (age < 0 || age > 150) {
    return { valid: false, error: "Age must be between 0 and 150" };
  }
  
  // Check if integer
  if (!Number.isInteger(age)) {
    return { valid: false, error: "Age must be a whole number" };
  }
  
  return { valid: true, value: age };
}

// Test cases
console.log(validateAge("25"));      // { valid: true, value: 25 }
console.log(validateAge(""));        // { valid: false, error: "Age is required" }
console.log(validateAge("abc"));     // { valid: false, error: "Age must be a number" }
console.log(validateAge("25.5"));    // { valid: false, error: "Age must be a whole number" }
console.log(validateAge("-5"));      // { valid: false, error: "Age must be between 0 and 150" }

Example 2: Safe Mathematical Operations

function safeDivide(a, b) {
  // Convert to numbers
  const numA = Number(a);
  const numB = Number(b);
  
  // Validate conversions
  if (isNaN(numA) || isNaN(numB)) {
    return null;  // or throw error
  }
  
  // Check for division by zero
  if (numB === 0) {
    return null;  // or return Infinity, or throw error
  }
  
  return numA / numB;
}

console.log(safeDivide(10, 2));      // 5
console.log(safeDivide("10", "2"));  // 5 (converts strings)
console.log(safeDivide(10, 0));      // null (division by zero)
console.log(safeDivide("abc", 2));   // null (invalid number)

Example 3: Default Values with Proper Handling

// ❌ Bad - 0 and "" are falsy
function greet(name) {
  name = name || "Guest";
  return `Hello, ${name}!`;
}

console.log(greet("John"));   // "Hello, John!"
console.log(greet(""));       // "Hello, Guest!" (intended?)
console.log(greet(0));        // "Hello, Guest!" (unexpected!)

// ✅ Good - explicit null/undefined check
function greet(name) {
  name = (name !== undefined && name !== null) ? name : "Guest";
  return `Hello, ${name}!`;
}

// ✅ Better - using nullish coalescing (ES2020)
function greet(name) {
  name = name ?? "Guest";  // Only replaces null/undefined
  return `Hello, ${name}!`;
}

console.log(greet("John"));   // "Hello, John!"
console.log(greet(""));       // "Hello, !" (empty string preserved)
console.log(greet(0));        // "Hello, 0!" (0 preserved)
console.log(greet(null));     // "Hello, Guest!"
console.log(greet(undefined));// "Hello, Guest!"

Type Conversion Cheat Sheet

Quick Reference Table

FromTo StringTo NumberTo Boolean
"123""123"123true
"123.45""123.45"123.45true
"abc""abc"NaNtrue
""""0false
"0""0"0true
0"0"0false
1"1"1true
-1"-1"-1true
true"true"1true
false"false"0false
null"null"0false
undefined"undefined"NaNfalse
[]""0true
[1,2,3]"1,2,3"NaNtrue
{}"[object Object]"NaNtrue
NaN"NaN"NaNfalse

Operator Coercion Summary

// + with string → string concatenation
"5" + 3"53"
5 + "3""53"

// - * / % → numeric operation
"5" - 32
"5" * 315
"10" / "2"5
"10" % 31

// == → coerces types
5 == "5"true
0 == falsetrue
null == undefinedtrue

// === → no coercion
5 === "5"false
0 === falsefalse
null === undefinedfalse

// if, while, for → boolean coercion
if ("hello") → true
if (0) → false
if ([]) → true

Practice Exercise

Create a file called type-conversion-practice.js:

// Type Conversion Practice

// Exercise 1: Create a function that safely adds two values
function safeAdd(a, b) {
  // TODO: Convert to numbers, validate, and add
  // Return null if conversion fails
}

// Test cases
console.log(safeAdd(5, 3));        // Should be 8
console.log(safeAdd("5", "3"));    // Should be 8
console.log(safeAdd("5", 3));      // Should be 8
console.log(safeAdd("abc", 3));    // Should be null
console.log(safeAdd(null, 5));     // Should be null

// Exercise 2: Create a function that checks if a value is "empty"
function isEmpty(value) {
  // TODO: Return true for: null, undefined, "", [], {}
  // Return false for everything else
}

// Test cases
console.log(isEmpty(null));        // Should be true
console.log(isEmpty(undefined));   // Should be true
console.log(isEmpty(""));          // Should be true
console.log(isEmpty([]));          // Should be true
console.log(isEmpty({}));          // Should be true
console.log(isEmpty(0));           // Should be false
console.log(isEmpty(false));       // Should be false
console.log(isEmpty("hello"));     // Should be false

// Exercise 3: Parse user input with proper error handling
function parseUserNumber(input) {
  // TODO: Convert input to number
  // Return object: { success: true, value: number } or { success: false, error: string }
}

// Test cases
console.log(parseUserNumber("123"));     // { success: true, value: 123 }
console.log(parseUserNumber("123.45"));  // { success: true, value: 123.45 }
console.log(parseUserNumber(""));        // { success: false, error: "Empty input" }
console.log(parseUserNumber("abc"));     // { success: false, error: "Invalid number" }
console.log(parseUserNumber(null));      // { success: false, error: "Null or undefined" }

Solutions will be provided in the next lesson!


Key Takeaways

Congratulations! You now understand type conversion and coercion in JavaScript. Here's what you learned:

Type Conversion - Manual conversion between types

  • String(): Convert to string
  • Number(): Convert to number
  • Boolean(): Convert to boolean
  • parseInt()/parseFloat(): Parse numbers from strings

Type Coercion - Automatic conversion by JavaScript

  • + with strings → concatenation
  • -, *, /, % → numeric operation
  • Comparisons and logical operators → boolean coercion

Truthy/Falsy Values

  • Only 7 falsy values: false, 0, -0, 0n, "", null, undefined, NaN
  • Everything else is truthy

Comparison Operators

  • == performs type coercion (avoid)
  • === does not coerce (use this!)

Best Practices

  • Always use === instead of ==
  • Be explicit with type conversions
  • Validate input types
  • Handle edge cases (null, undefined, NaN)
  • Use Number.isNaN() instead of isNaN()

Common Pitfalls

  • "5" + 3 = "53" (concatenation)
  • "5" - 3 = 2 (numeric)
  • Empty string converts to 0
  • Empty array/object are truthy
  • NaN !== NaN

What's Next?

Excellent work! 🎉 You now have a solid understanding of type conversion and coercion.

In the next lesson, we'll explore Operators and Expressions where you'll learn:

  • Arithmetic operators (+, -, *, /, %, **)
  • Assignment operators (=, +=, -=, etc.)
  • Comparison operators (==, ===, !=, !==, <, >, <=, >=)
  • Logical operators (&&, ||, !)
  • Operator precedence and associativity
  • Advanced operator patterns

Understanding type conversion is essential for mastering operators!