Just like their built-in error types, we can create and throw our own custom error types. Why? Well, because it will give us more control and flexibility to design error handling systems fit exactly for our app's needs. Let me show you what I mean by that.
Let's pretend we're creating an app that needs to accept payments from customers. Well, there's always going to be room for error when you're accepting payments. So let's create a custom payment error type. We do this by using the class keyword, giving the error type a name, in this case payment error, and then you should extend the base error class.
This is that error class that we've already thrown before. And whoops, that should be the extends keyword plural. Okay, the next thing we need to do is provide a constructor function that takes in a message. This is the function that runs whenever you new up a new payment error.
or you throw it with that new keyword in there. We'll call the base error constructor with the super keyword. Don't worry if you don't really know what that means. This is just a pattern that you can do over and over again.
And then we'll set the name of this payment error type to payment error. This isn't strictly necessary, but the base error class here does have a name property on it equal to simply error. So in order to be consistent, it's good for our class to have a name which matches its actual class name. Next, I'm going to copy and paste a dummy function for our users to make a payment.
In a real life application, this function would make a call to some payment gateway or payment API. But for our purposes, I am just dummying out here some available funds as well as a payment amount. and saying that if the payment is greater than the available funds, then we're gonna throw a new payment error saying that the user tried to pay so much with the available funds of X. So obviously this is going to fail because they don't have enough funds to make the payment.
If everything goes well, we just return a string that says payment successful. Okay. Let's wrap this call of make payment in a try catch to simulate a user making a real payment. Then if their payment fails, we should, of course, let them know about that.
So I'll use the built-in alert function, though in your real apps, you'll probably want something a little bit prettier. Over in the browser, the script runs, and sure enough, our catch catches that error and then alerts the user. User tried to pay 60 with available funds, 50. This isn't bad, but we can make it even better.
Let's say that we want to handle different reasons for payment failures differently. For example, maybe we want to show an alert just like we do here if the user has insufficient funds, but if they have an invalid card number, we want to highlight the card number input red with a message underneath it. I know that we don't actually have that on our page. But you've seen this UI example over and over again, right?
Essentially an invalid form field. Well, we could check the message here that the payment error provides, but that gives us a lot less flexibility to have precise and most helpful messages with dynamic content included in it. To fix this, let's introduce the concept of an error code to our custom payment error. This will just be a new property on the object that we'll set by accepting a code argument to the constructor.
code to the provided code. Perfect. Now when we go to throw the payment error, we should also provide a custom code. Now if we were working with TypeScript, we could make this accept only certain strings.
But let's say that we're accepting, I don't know, codes like insufficient funds, invalid card, etc. So the code that we pass here would be insufficient funds. Down inside of our catch block now, we have control over what we do based on the code of the payment error. For example, we could use a conditional here to check, is the error code insufficient funds?
and if it is, it will show an alert to the user. But if the error code is invalid card, then we want to do a more standard form-filled validation feedback where you highlight the input red and have a little message underneath it. This is getting fairly robust, but there's still more ways we could extend the payment error. How?
Well, let's make a distinction between the error message that's geared towards helping the development team understand the issue and the user-facing message. Right now, this isn't the most user-friendly way to say this, but it is super useful for our development team to understand what's going on. They can see, hey, the error code is insufficient funds, and sure enough, that's true because there were only $50 in the user's account, and they tried to pay for a $60 item. but this doesn't read well for the end user.
No worries. Let's add a new property to the payment error called userMessage. Now, before we set the value on this, let's create an object up here that maps our error codes to some user-friendly messages. I'll call it codeMessageMap, where the insufficient funds code maps to the message you do not have enough funds to complete this payment.
and then the invalid card code maps to the message the card number you entered is invalid. Then we'll set the user message on the payment error to the message in the code message map that lines up with the provided error code. If there isn't a user-friendly message for the provided error code, then we'll just provide the generic message an error occurred with your payment. This time in our try-catch, the message that we show to our user will be the user message.
And then the message we log for our development purposes will be that original message. On page load, the user does get alerted that you do not have enough funds to complete this payment. If I click OK, it looks like we get the user tried to pay 60 with available funds 50 again. That's because I haven't taken off this other alert of error dot message.
Let's just remove that. And give the page a refresh. Yep, there is the user friendly message. And in the console where our developers will look, we get the message geared towards them.
Finally, what if some other type of error is thrown from the make payment function, like one of the built-in error types, something other than a payment error. Let's simulate that by forcing a reference error inside of make payment. We can do that by trying to read from a non-existent transaction object. Okay, save the code, refresh the page, and notice that I get zero indication that anything is wrong.
That's because a normal reference error doesn't have a code property on it. Therefore, we need a way to distinguish between payment errors and the built-in reference error. Well, you can easily check for what type of error is thrown using the instance of keyword. Here, let's say that if error is an instance of payment error, then we do our payment error stuff.
Otherwise, we'll alert an extremely generic error message and console log the reference error message to the console for our developer sync. This time in the browser, the user gets the message, something went wrong, please contact support. and our developers get more detailed information about the code. To sum up, you can create your own custom error types to make error handling in your applications more robust and fit to the use case at hand.