DEV Community

Cover image for A developer's guide to JavaScript errors & writing robust code πŸ’»πŸ˜Ž
Nayana
Nayana

Posted on

A developer's guide to JavaScript errors & writing robust code πŸ’»πŸ˜Ž

As developers, we strive for elegant, error-free code. However, the reality of software development inevitably involves encountering errors. In JavaScript, understanding the different types of errors and knowing how to handle them gracefully is crucial for building robust and user-friendly applications.

This blog post will explore the common error types in JavaScript, illustrate them with code examples, and then delve into effective error handling techniques.

Understanding the Landscape of JavaScript Errors

JavaScript categorizes errors into several types, each signaling a specific kind of problem. Let's explore the most common ones:

1. SyntaxError:

These errors occur when your JavaScript code violates the language's grammar rules. The JavaScript engine detects these errors during the parsing phase, before the code is executed.

// Example of SyntaxError: Missing closing parenthesis
console.log("Hello, world!";

// Output (in the console):
Uncaught SyntaxError: missing ) after argument list
Enter fullscreen mode Exit fullscreen mode

2. ReferenceError:

A ReferenceError arises when you try to access a variable that hasn't been declared or is out of scope.

// Example of ReferenceError: Accessing an undeclared variable
console.log(myVariable);

// Output (in the console):
Uncaught ReferenceError: myVariable is not defined
Enter fullscreen mode Exit fullscreen mode

3. TypeError:

TypeError indicates an operation that was performed on a value of an unexpected type. This often happens when you call a method or access a property that doesn't exist for the given type.

// Example of TypeError: Calling a method on an undefined value
let myString = undefined;
console.log(myString.toUpperCase());

// Output (in the console):
Uncaught TypeError: Cannot read properties of undefined (reading 'toUpperCase')
Enter fullscreen mode Exit fullscreen mode

4. RangeError:

A RangeError occurs when a numeric variable or parameter is outside of its permitted range.

// Example of RangeError: Invalid array length
let myArray = new Array(-5);

// Output (in the console):
Uncaught RangeError: Invalid array length
Enter fullscreen mode Exit fullscreen mode

5. URIError:

URIError is thrown when there's an issue with encoding or decoding Uniform Resource Identifiers (URIs).

// Example of URIError: Invalid URI component
try {
  decodeURI('%invalid_uri%');
} catch (error) {
  console.error(error);
}

// Output (in the console):
URIError: URI malformed
Enter fullscreen mode Exit fullscreen mode

6. EvalError (Legacy):

This error was used in older versions of JavaScript concerning the eval() function. It's largely deprecated in modern JavaScript environments.

7. InternalError:

An InternalError signifies an error within the JavaScript engine itself. This can occur due to issues like excessive recursion or very large data structures.

// Example of InternalError (can be difficult to reproduce reliably)
function recursiveFunction() {
  recursiveFunction(); // Infinite recursion
}

try {
  recursiveFunction();
} catch (error) {
  console.error(error);
}

// Output (may vary depending on the browser/environment):
InternalError: too much recursion
Enter fullscreen mode Exit fullscreen mode

8. Logical Errors (Not a specific error type):

While not a distinct error type like the others, logical errors are perhaps the most common and frustrating. They occur when your code executes without throwing an exception but produces an unexpected or incorrect result due to flaws in your logic.

// Example of Logical Error: Incorrect calculation
function calculateAverage(a, b, c) {
  return a + b + c / 3; // Incorrect order of operations
}

let average = calculateAverage(10, 20, 30);
console.log(average); // Output: 40 (Incorrect, should be 20)
Enter fullscreen mode Exit fullscreen mode

The Art of Error Handling: Gracefully Navigating the Unexpected

Now that we understand the different types of errors, let's explore how to handle them effectively in JavaScript. The primary mechanism for error handling is the try...catch statement.

1. The try...catch Statement:

The try block encloses the code that might throw an error. If an error occurs within the try block, the execution immediately jumps to the catch block.

try {
  // Code that might throw an error
  let result = undefinedVariable.toUpperCase();
  console.log("This will not be executed if an error occurs.");
} catch (error) {
  // Code to handle the error
  console.error("An error occurred:", error.message);
  // You can also access other properties of the error object, like error.name
}

// Output (in the console):
// An error occurred: undefinedVariable is not defined
Enter fullscreen mode Exit fullscreen mode

Explanation:

try block: The code inside this block is monitored for exceptions.

catch block: If an error is thrown in the try block, the JavaScript engine immediately executes the code within the catch block.

error parameter: The catch block receives an error object as a parameter. This object contains information about the error, including:

message: A human-readable description of the error.

name: The name of the error type (e.g., "ReferenceError", "TypeError").

stack: A string representing the call stack at the point the error occurred (useful for debugging).

2. The finally Block:

In addition to try and catch, you can also include a finally block. The code inside the finally block will always execute, regardless of whether an error occurred in the try block or not. This is useful for cleanup tasks.

function processData(data) {
  let file;
  try {
    file = openFile(data); // Assume this function might throw an error
    // Process the file
    console.log("File processed successfully.");
  } catch (error) {
    console.error("Error processing file:", error.message);
  } finally {
    if (file) {
      closeFile(file); // Ensure the file is closed, even if an error occurred
      console.log("File closed.");
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

3. Throwing Errors Manually:

You can also explicitly throw errors in your code using the throw statement. This is useful for signaling specific problems or validating input.

function validateAge(age) {
  if (age < 0) {
    throw new RangeError("Age cannot be negative.");
  }
  console.log("Age is valid:", age);
}

try {
  validateAge(-5);
} catch (error) {
  console.error("Validation error:", error.message);
}

// Output (in the console):
Validation error: Age cannot be negative.
Enter fullscreen mode Exit fullscreen mode

Best Practices for Error Handling:

Be Specific: Try to catch specific error types when possible to handle different scenarios appropriately.

Log Errors: Use console.error() or a logging library to record errors for debugging and monitoring. Include relevant context in your error messages.

Provide User-Friendly Feedback: Don't expose raw error messages to users. Instead, display informative and helpful messages.

Avoid Catching Too Broadly: Catching all exceptions without handling them appropriately can mask underlying issues.

Use finally for Cleanup: Ensure resources are released or cleanup tasks are performed, regardless of errors.

Validate Input: Proactively validate input data to prevent errors before they occur.

Implement Global Error Handlers: For browser applications, you can use window.onerrorto catch unhandled errors globally. In Node.js, you can use process.on('uncaughtException').

// Example of a global error handler in a browser
window.onerror = function(message, source, lineno, colno, error) {
  console.error("Global error caught:", message, source, lineno, colno, error);
  // Optionally, display a user-friendly message
  alert("Oops! Something went wrong. Please try again later.");
  return true; // Prevents the default browser error reporting
};
Enter fullscreen mode Exit fullscreen mode

Top comments (0)