Books / TypeScript for Java Developers by CodeAhoy / Chapter 6
Exception Handling
Exception handling in TypeScript is similar to exception handling in Java that’s based on the try/catch syntax. It allows you to handle runtime errors in your code in a graceful manner by escaping the normal flow of the program in the presence of errors by giving control to a different section of the code that can do damage control.
Exception Handling Basics
In both TypeScript and Java, you can use try-catch
blocks to handle exceptions. To recall, a try-catch
block:
- consists of a
try
block, which contains the code that could throw an exception - a
catch
block, which contains the code that handles the exception thrown from the try block - an optional
finally
block
Here’s how we write code in Java to handle exceptions:
try {
// code that may throw an exception
} catch (Exception e) {
// code to handle the thrown exception
}
TypeScript syntax is very similar. This is how we’d write the try-catch in TypeScript:
try {
// code that may throw an exception
} catch (error) {
// code to handle the thrown exception
}
The exception itself is an object, which is passed to the catch
block as an argument (in this case error
). In Java, all exceptions must be a subtype of the Throwable
class, which sit at the root of the exception hierarchy in Java. In TypeScript, the thrown exception can be any type, including primitives, objects, and even functions.
// Throwing a string
function divideThrowString(x: number, y: number): number {
if (y === 0) {
throw "Cannot divide by zero"; // throw a string
}
return x / y;
}
try {
console.log(divideThrowString(10, 0));
} catch (e) {
console.error(e); // Outputs "Cannot divide by zero"
}
// Throwing an object
function divideThrowObject(x: number, y: number): number {
if (y === 0) {
throw { message: "Cannot divide by zero" };
}
return x / y;
}
try {
console.log(divideThrowObject(10, 0));
} catch (e) {
console.error(e.message); // Outputs "Cannot divide by zero"
}
// Throwing a function
function divideThrowFunction(x: number, y: number): number {
if (y === 0) {
throw function() { console.log("There has been an error") };
}
return x / y;
}
try {
console.log(divideThrowFunction(10, 0));
} catch (e) {
e(); // Outputs "There has been an error"
}
At this point you may wonder what’s the type of the variable in catch
e.g error
or e
. If you don’t specify a type, the type of the variable in the catch block is inferred from the type of the thrown exception.
try {
throw new Error("I'm an error Object");
} catch (error) {
console.log(typeof error); // Outputs "object"
}
try {
throw "I'm a String";
} catch (error) {
console.log(typeof error); // Outputs "string"
}
Recall that TypeScript compiles its code to JavaScript. For this reason, there’s no way for TypeScript compiler to know at runtime what type of exception will be passed to the catch block. For this reason, the type of error
variable in the catch block must be of type any
or undefined
. E.g.
try {
throw new Error("Something went wrong");
} catch (error: any) {
console.log(error.message); // Outputs "Something went wrong"
}
To handle multiple types of exceptions, you can use union types e.g. Error | any
as shown in the example below:
try {
throw new Error("Something went wrong");
} catch (error: Error | any) {
if (error instanceof Error) {
console.log(error.message); // Outputs "Something went wrong"
} else {
console.log(error);
}
}
TypeScript doesn’t support support multiple catch blocks like Java. To handle different types of exceptions, you must use if... else if... else
inside the single catch block.
Like Java, in TypeScript the finally
block is optionally used in a try-catch
statement to specify a block of code that’s always executed when the try
block exits. The finally
block is useful for cleaning up resources.
try {
// Code that may throw an exception is here
} catch (error) {
// Code to handle the exception goes here
} finally {
// Code to be executed goes here when try exits.
}