JS

JavaScript Fundamentals

25 lessons

Progress0%
1. Introduction to JavaScript
1What is JavaScript?2Setting Up Your Environment
2. Variables and Data Types
1Declaring Variables2Data Types3Type Conversion
3. Operators
Arithmetic OperatorsComparison OperatorsLogical Operators
4. Control Flow
Conditional StatementsLoops
5. Functions
Function Basics
6. Arrays & Iteration
Array MethodsSpread, Rest & Destructuring
7. Objects & JSON
Working with ObjectsJSON & Optional Chaining
8. OOP & Classes
Class BasicsInheritance & Private Fields
9. Modules & Modern JS
ES ModulesModern JavaScript Features
10. Async JavaScript
PromisesAsync/Await
11. Error Handling
Error Types & try/catchCustom Errors & Debugging
12. Iterators & Advanced
Iterators & GeneratorsMap, Set & WeakRefs
All Tutorials
JavaScriptError Handling
Lesson 22 of 25 min
Chapter 11 · Lesson 1

Error Types & try/catch

Error Types & try/catch

Robust programs anticipate failure. JavaScript has a rich error hierarchy and a structured mechanism for handling exceptions.

Built-in Error Types

JavaScript ships with several specialised Error subclasses:

TypeTrigger
ErrorGeneric base class
TypeErrorOperation on the wrong type (e.g., calling a non-function)
RangeErrorValue outside an allowed range (e.g., invalid array length)
ReferenceErrorAccessing an undeclared variable
SyntaxErrorMalformed code (usually at parse time)
URIErrorMalformed URI in encodeURI / decodeURI
EvalErrorDeprecated; from eval()

try / catch / finally

js
try {
  // code that might throw
} catch (err) {
  // handle the error; err is the thrown value
} finally {
  // always runs — cleanup goes here
}

finally runs regardless of whether an exception was thrown, even if a return statement was executed inside try or catch.

Re-throwing Errors

Only catch what you can handle. Re-throw anything you don't understand:

js
try {
  riskyOp();
} catch (err) {
  if (err instanceof NetworkError) {
    handleNetworkError(err);
  } else {
    throw err; // propagate unexpected errors
  }
}

Error Properties

Every Error object has:

  • message — human-readable description
  • name — error type name ("TypeError", "RangeError", etc.)
  • stack — stack trace string (non-standard but universal in practice)

Throwing Non-Error Values

You can technically throw anything (string, number, object), but always throw Error instances so that callers get a stack trace and can use instanceof.

Code Examples

Built-in Error Typesjavascript
function demonstrateErrors() {
  const errors = [
    () => null.property,          // TypeError
    () => undeclaredVar,          // ReferenceError
    () => new Array(-1),          // RangeError
    () => JSON.parse("{bad}"),    // SyntaxError
  ];

  errors.forEach((fn, i) => {
    try {
      fn();
    } catch (err) {
      console.log(`Error ${i + 1}: [${err.name}] ${err.message}`);
    }
  });
}

demonstrateErrors();

Each built-in Error subclass signals a distinct category of failure. Checking err.name or using instanceof lets you handle each type differently.

try/catch/finally and Re-throwingjavascript
function parseConfig(json) {
  let parsed;
  try {
    parsed = JSON.parse(json);
    if (typeof parsed !== "object" || parsed === null) {
      throw new TypeError("Config must be an object");
    }
    if (!parsed.version) {
      throw new RangeError("Config must have a version");
    }
    return parsed;
  } catch (err) {
    if (err instanceof SyntaxError) {
      console.error("Invalid JSON:", err.message);
      return null;
    }
    throw err; // re-throw TypeError, RangeError, etc.
  } finally {
    console.log("parseConfig attempt completed");
  }
}

// Test cases
console.log(parseConfig('{"version": 2}'));
console.log(parseConfig("{invalid}"));
try {
  parseConfig("null");
} catch (e) {
  console.log("Caught re-thrown:", e.constructor.name, e.message);
}

finally always runs, making it ideal for cleanup. Selective re-throwing lets higher-level code handle errors it understands, while unexpected errors bubble up naturally.

Error Properties and Stack Tracesjavascript
function inner() {
  throw new RangeError("Value out of bounds: 150 not in [0, 100]");
}

function middle() {
  inner();
}

function outer() {
  try {
    middle();
  } catch (err) {
    console.log("name:   ", err.name);
    console.log("message:", err.message);
    // Stack traces vary by environment; just show first line
    const firstLine = err.stack.split("\n")[0];
    console.log("stack[0]:", firstLine);

    // Check type
    console.log("is RangeError:", err instanceof RangeError);
    console.log("is Error:     ", err instanceof Error);
  }
}

outer();

Every Error has name, message, and stack properties. The stack trace shows the call chain from the throw site. instanceof works hierarchically — RangeError is also an Error.

Quick Quiz

1. When does the 'finally' block execute?

2. Which error is thrown when you try to access a property on null?

3. What is the best practice when catching an error you cannot fully handle?

4. What three properties does every Error object have?

Was this lesson helpful?

PreviousNext