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 23 of 25 min
Chapter 11 · Lesson 2

Custom Errors & Debugging

Custom Errors & Debugging

Creating custom error classes makes error handling more semantic and powerful. The console object provides far more debugging tools than just console.log.

Extending Error

Create domain-specific error types by extending the built-in Error class:

js
class ValidationError extends Error {
  constructor(message, field) {
    super(message);       // set this.message
    this.name = "ValidationError";
    this.field = field;   // custom property
  }
}

Always call super(message) and set this.name manually — otherwise name stays as "Error".

Why Custom Errors?

  • instanceof checks become meaningful: err instanceof ValidationError
  • Carry extra context (field, statusCode, requestId) alongside the message
  • Catch blocks can discriminate between error types precisely

Error Hierarchy Pattern

js
class AppError extends Error { ... }
class DatabaseError extends AppError { ... }
class NotFoundError extends DatabaseError { ... }

A single catch (err) { if (err instanceof AppError) ... } catches all domain errors.

Console Methods

Most developers use only console.log. Here is the full toolkit:

MethodPurpose
console.logGeneral output
console.warnWarning (yellow in DevTools)
console.errorError (red, includes stack in DevTools)
console.tableRender arrays/objects as a table
console.time / timeEndMeasure elapsed time
console.group / groupEndCollapsible log groups
console.assertLog only if condition is false
console.dirInspect object properties interactively
console.countCount how many times a label is reached
console.tracePrint a stack trace at the current position

Code Examples

Custom Error Classesjavascript
class AppError extends Error {
  constructor(message, code) {
    super(message);
    this.name = "AppError";
    this.code = code;
  }
}

class ValidationError extends AppError {
  constructor(message, field) {
    super(message, "VALIDATION_ERROR");
    this.name  = "ValidationError";
    this.field = field;
  }
}

class NotFoundError extends AppError {
  constructor(resource, id) {
    super(`${resource} with id ${id} not found`, "NOT_FOUND");
    this.name     = "NotFoundError";
    this.resource = resource;
  }
}

function processRequest(data) {
  if (!data.name) throw new ValidationError("Name is required", "name");
  if (data.id === 0) throw new NotFoundError("User", 0);
  return { ok: true };
}

const testCases = [
  { id: 1, name: "Alice" },
  { id: 1 },              // missing name
  { id: 0, name: "Bob" }, // not found
];

testCases.forEach((tc) => {
  try {
    console.log(JSON.stringify(processRequest(tc)));
  } catch (err) {
    if (err instanceof ValidationError) {
      console.log(`Validation [${err.field}]: ${err.message}`);
    } else if (err instanceof NotFoundError) {
      console.log(`Not found [${err.resource}]: ${err.message}`);
    } else {
      throw err;
    }
  }
});

Custom error classes carry structured data (field, resource, code) alongside the message. instanceof discrimination in catch blocks enables clean, type-safe error routing without inspecting message strings.

Console Debugging Toolkitjavascript
const users = [
  { id: 1, name: "Alice", role: "admin",  score: 95 },
  { id: 2, name: "Bob",   role: "user",   score: 72 },
  { id: 3, name: "Carol", role: "user",   score: 88 },
];

// console.table: ideal for arrays of objects
console.table(users);

// console.time / timeEnd: performance measurement
console.time("filter+map");
const adminNames = users
  .filter((u) => u.role === "admin")
  .map((u) => u.name);
console.timeEnd("filter+map");
console.log("Admins:", adminNames);

// console.group: collapsible sections
console.group("User Report");
users.forEach((u) => {
  console.log(`  ${u.name} (${u.role}): ${u.score}`);
});
console.groupEnd();

// console.assert: only logs when condition is false
console.assert(users.length > 0, "Expected at least one user");
console.assert(users.length > 10, "Expected more than 10 users — THIS LOGS");

console.table gives a readable snapshot of tabular data. console.time/timeEnd measures micro-benchmarks. console.group organises related output. console.assert fires only when the condition is false — useful for invariant checks.

Error Hierarchy and Catching by Typejavascript
class HttpError extends Error {
  constructor(status, message) {
    super(message);
    this.name   = "HttpError";
    this.status = status;
  }
}

class AuthError extends HttpError {
  constructor() { super(401, "Unauthorized — please log in"); }
}

class ForbiddenError extends HttpError {
  constructor() { super(403, "Forbidden — insufficient permissions"); }
}

function handleError(err) {
  if (err instanceof AuthError)      console.log("[AUTH]   ", err.message);
  else if (err instanceof ForbiddenError) console.log("[FORBID] ", err.message);
  else if (err instanceof HttpError) console.log(`[HTTP ${err.status}]`, err.message);
  else                               console.log("[UNKNOWN]", err.message);
}

[
  new AuthError(),
  new ForbiddenError(),
  new HttpError(404, "Not Found"),
  new Error("Unexpected crash"),
].forEach(handleError);

Hierarchical error classes let catch logic be both precise and broad. instanceof checks flow from specific to general, so AuthError (subclass) is caught before the broader HttpError check.

Quick Quiz

1. Why should you set this.name manually in a custom Error subclass?

2. What does console.assert(condition, message) do?

3. Which console method is best for displaying an array of objects as a formatted table?

Was this lesson helpful?

PreviousNext