TypeScript Exception Handling (+ Custom Exceptions)

Exception handling in TypeScript is similar to JavaScript, where we use ‘try‘, ‘catch‘, and optionally ‘finally‘ blocks to handle errors. This is pretty much similar, and with little differences, to other popular programming languages such as Java.

Additionally, TypeScript doesn’t provide specific exception types, but we can use built-in error classes or define custom error classes for different error scenarios and later handle them.

1. Basic Exception Handling in TypeScript

Here’s a typical program structure for exception handling in TypeScript. By default, TypeScript allows every exception to be of type any or unknown.

try {
    const result = JSON.parse("invalidJSON");
    // Handle result
} catch (error: any) {
    console.error("An error occurred:", error.message);
} finally {
    console.log("Cleanup after try-catch");
}

Program output:

[ERR]: "An error occurred:",  "Unexpected token 'i', "invalidJSON" is not valid JSON" 
[LOG]: "Cleanup after try-catch"

To catch specific types of errors, we use the ‘instanceof‘ operator within the catch block.

Here’s an example of catching specific error types. Note that the error types (SyntaxError and TypeError) are based on the built-in error classes provided by JavaScript.

try {
  const result = JSON.parse("invalidJSON");
  // Handle result
} catch (error) {
  if (error instanceof SyntaxError) {
    console.error("SyntaxError:", error.message);
  } else if (error instanceof TypeError) {
    console.error("TypeError:", error.message);
  } else {
    console.error("Unknown error:", error.message);
  }
} finally {
    console.log("Cleanup after try-catch");
}

Program output:

[ERR]: "SyntaxError:",  "Unexpected token 'i', "invalidJSON" is not valid JSON" 
[LOG]: "Cleanup after try-catch"

2. Throwing and Handling Custom Exceptions

To catch specific types of errors, we can use the instanceof operator within the catch block. However, TypeScript doesn’t have dedicated exception types, so we essentially check for error objects based on their class or prototype.

2.1. Creating Custom Exceptions

Custom exceptions are helpful when we want the exceptions to represent a specific failure event during the normal processing of the business logic.

We can create these custom exceptions by defining our own classes that extend the built-in Error class or any of its subclasses.

For example, while processing a user-submitted form, we may need to process the Date fields. Many things can go wrong when the user submits a Date. For example:

  • A date may not be in a requirement pattern. Let us call it: InvalidDateFormatError.
  • A date may be in the future when the past date is requested. Let us call it: DateIsInTheFutureError.
class InvalidDateFormatError extends RangeError {}
class DateIsInTheFutureError extends RangeError {}

2.2. Throwing Custom Exceptions

After we have created the custom exception classes, we can throw them from the application code as a normal exception, as we do in the other programming languages.

To throw a custom exception, simply create an instance of the custom error class and throw it using the throw statement.

class InvalidDateFormatError extends RangeError {}
class DateIsInTheFutureError extends RangeError {}

function parseDate(birthday: string): Date {

  let date = new Date(birthday);

  if (!(Object.prototype.toString.call(date) === '[object Date]' && !Number.isNaN(date.getTime()))) {
    throw new InvalidDateFormatError('Enter a date in the form YYYY/MM/DD');
  }
  if (date.getTime() > Date.now()) {
    throw new DateIsInTheFutureError('Entered Date is the future');
  }
  return date
}

2.3. Catching Custom Exceptions or Error Types

We can catch a custom exception like any other error by using a ‘try…catch‘ block. We use the instanceof operator to specifically catch instances of the custom error classes.

class InvalidDateFormatError extends RangeError {}
class DateIsInTheFutureError extends RangeError {}

function parseDate(birthday: string): Date {

  let date = new Date(birthday);

  if (!(Object.prototype.toString.call(date) === '[object Date]' && !Number.isNaN(date.getTime()))) {
    throw new InvalidDateFormatError('Enter a date in the form YYYY/MM/DD');
  }
  if (date.getTime() > Date.now()) {
    throw new DateIsInTheFutureError('Entered Date is the future');
  }
  return date
}

try {
    const result : Date= parseDate("1987/11/12");
    console.info('Date is', result.toISOString())
} catch (error: unknown) {

    if (error instanceof InvalidDateFormatError) {
        console.error("InvalidDateFormatError:", error.message);
    } 
    else if (error instanceof DateIsInTheFutureError) {
        console.error("DateIsInTheFutureError:", error.message);
    } 
    else {
        console.error('Unexpected Error');
    }
}

Let’s test the above code using invalid input values. For example, when we pass the date value as “1987/31/12” then it will throw the InvalidDateFormatError error because ‘31‘ is not a valid month value.

//parseDate("1987/31/12")

InvalidDateFormatError: Enter a date in the form YYYY/MM/DD

Similarly, when we pass a future date such as ‘2087/11/12‘ then we should get the DateIsInTheFutureError error.

//parseDate("2087/11/12")

DateIsInTheFutureError: Entered Date is the future

4. Conclusion

In this TypeScript tutorial, we discussed the error handling mechanisms using try, catch, finally blocks and throw statements. We looked at the built-in error types as well as the custom exception types.

Custom exceptions are useful for adding context to the error messages. It helps to do more than just signal that something failed, a custom error indicates why it failed. On rainy days, it helps in debugging the production issues much faster.

Happy Learning !!

Comments

Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.