Custom Error Classes
Introduction: When Things Go Wrong
In our previous lesson, we learned how to use inheritance to build specialized classes like a SavingsAccount. We saw how the extends keyword allows one class to take on the features of another. While building features is exciting, professional programming also requires us to plan for moments when things do not go as expected. For example, what happens if a user tries to withdraw more money than they have or provides a negative number as an amount?
In this lesson, we will apply our knowledge of inheritance to the built-in JavaScript Error class. We will create a custom DomainError class to handle specific problems in our application logic. By the end of this lesson, you will know how to create your own error types, how to throw them when rules are broken, and how to catch them gracefully so your program keeps running instead of crashing.
Why Built-in Errors Fall Short
By default, JavaScript provides a generic Error object. You can create one by saying new Error("Something went wrong"). This is fine for simple scripts, but in a large application, a simple text message is often not enough. If your code receives an error, it is very difficult to write logic that reacts differently to "Insufficient funds" versus "Database connection lost" if both are merely plain text strings.
Professional code needs structured information. We want our errors to carry specific codes or types that our program can read and act upon. This allows us to distinguish between a mistake made by a user and a serious system failure. Rather than merely showing a message, we can use these codes to decide whether to ask the user to try again or alert a developer that the server is down.
Creating A Custom Error Class
Since Error is a built-in class in JavaScript, we can use the extends keyword to create our own version of it. This is a direct application of the inheritance principles we practiced in the previous lesson. When we create our DomainError class, we call super(message) to let the original Error class handle the description. We then add our own properties, like a code, to make the error more useful.
In this example, the constructor takes a message and a specific error code. By setting this.name, we make it clear that this is not merely a standard error, but a specific type we created for our business logic. This custom class will now have all the standard features of an error, such as a "stack trace" that shows where the error occurred, while also carrying our custom code property.
Throwing Custom Errors From Class Methods
Once we have our custom error class, we can use it inside our methods to enforce rules. We call these guard clauses. A guard clause checks if a condition is met at the very beginning of a method. If the condition is not met, we use the throw keyword to stop the execution of the method and send our custom error back to whoever called it.
The withdraw method above uses two guard clauses. If a user tries to withdraw a negative amount, we throw a DomainError with the code E_AMOUNT. If they try to take out more than they have, we throw one with E_FUNDS. This prevents the #balance from ever becoming an incorrect or impossible number. The throw keyword acts like a "super return" that exits the function immediately and looks for the nearest error handler.
Catching And Discriminating Errors
The Safe Run Pattern
Sometimes, writing try...catch blocks everywhere can make your code look messy and hard to read. A common pattern in professional JavaScript is to create a "helper" function that runs a task and returns a simple object telling you if it succeeded or failed for known errors. We often call this a safe run pattern.
Output:
The safeRun function takes another function as an argument. It attempts to run that function. If it succeeds, it returns an object with ok: true. If a DomainError is caught, it returns an object with ok: false and the error code. Notice that we still use throw e for unexpected errors. This ensures that while we handle our business rules gracefully, we don't accidentally "swallow" programming mistakes like typos or server crashes.
Summary And Practice Preview
In this lesson, we expanded our understanding of inheritance by extending the built-in Error class to create a custom DomainError. We learned how to add custom properties like error codes to provide more context when things go wrong. We explored how to use the throw keyword to protect our methods with guard clauses and how to use try...catch combined with instanceof to handle known errors while re-throwing unknown ones. Finally, we looked at the safe run pattern as a way to simplify error handling in our code.
These techniques are essential for building professional-grade software that is reliable and easy to debug. In the upcoming practice exercises, you will get to build your own custom error classes and implement the logic to catch and handle them. Happy coding!
