Error Handling in TypeScript
Learn about Error Handling in TypeScript with this detailed tutorial. Includes explanations and examples to help you master error handling.
Introduction
Error handling in TypeScript is crucial for building robust applications. TypeScript extends JavaScript's error handling capabilities with its type system, enabling you to catch errors early during development and ensuring type safety.
Basic Error Handling
TypeScript uses the same try-catch-finally mechanism as JavaScript for error handling.
function riskyFunction() {
throw new Error("Something went wrong");
}
try {
riskyFunction();
} catch (error) {
console.error("Caught an error:", error.message);
} finally {
console.log("Cleaning up...");
}
Explanation
In this example, the riskyFunction
throws an error, which is then caught in the try
block. The catch
block handles the error, and the finally
block executes cleanup code.
Typed Errors
You can create custom error classes to handle different types of errors in a more structured way.
class CustomError extends Error {
constructor(message: string) {
super(message);
this.name = "CustomError";
}
}
function riskyFunction() {
throw new CustomError("Custom error occurred");
}
try {
riskyFunction();
} catch (error) {
if (error instanceof CustomError) {
console.error("Caught a custom error:", error.message);
} else {
console.error("Caught an unknown error:", error);
}
}
Explanation
In this example, a custom error class CustomError
is created. The riskyFunction
throws an instance of this custom error, which is then handled in the catch
block using the instanceof
operator.
Asynchronous Error Handling
Handling errors in asynchronous code can be done using try-catch
within async functions or by attaching .catch
handlers to promises.
async function asyncFunction() {
throw new Error("Async error");
}
async function run() {
try {
await asyncFunction();
} catch (error) {
console.error("Caught an async error:", error.message);
}
}
run();
Explanation
In this example, the asyncFunction
throws an error, which is caught using a try-catch
block within another async function run
.
Error Handling with Fetch
Handling errors during HTTP requests can be done using try-catch
with async/await or .catch
with promises.
async function fetchData(url: string) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching data:", error.message);
throw error;
}
}
fetchData("https://api.example.com/data")
.then(data => console.log("Data:", data))
.catch(error => console.error("Caught in .catch:", error.message));
Explanation
In this example, the fetchData
function fetches data from a URL and handles errors using try-catch
. It also demonstrates how to handle errors using the .catch
method on the promise.
Strict Null Checks
TypeScript's strict null checks help prevent runtime errors by ensuring that values are properly checked before being used.
function getLength(str: string | null): number {
if (str === null) {
throw new Error("String is null");
}
return str.length;
}
try {
console.log(getLength("Hello")); // 5
console.log(getLength(null)); // Throws error
} catch (error) {
console.error("Caught an error:", error.message);
}
Explanation
In this example, the getLength
function checks if the input string is null
and throws an error if it is. This helps prevent null reference errors at runtime.
Using Optionals and Default Values
You can handle potential undefined values by using optional chaining and default values.
interface User {
name: string;
age?: number;
}
function printUserInfo(user: User) {
const age = user.age ?? "Unknown age";
console.log(`Name: ${user.name}, Age: ${age}`);
}
printUserInfo({ name: "Alice", age: 25 }); // Name: Alice, Age: 25
printUserInfo({ name: "Bob" }); // Name: Bob, Age: Unknown age
Explanation
In this example, the printUserInfo
function uses the nullish coalescing operator (??
) to provide a default value for the age
property if it is undefined
.
Custom Error Handling Logic
You can create custom error handling logic to provide more detailed and specific error messages.
class ValidationError extends Error {
constructor(message: string) {
super(message);
this.name = "ValidationError";
}
}
function validateUser(user: User) {
if (!user.name) {
throw new ValidationError("Name is required");
}
if (user.age && user.age < 0) {
throw new ValidationError("Age must be a positive number");
}
}
try {
validateUser({ name: "", age: -1 });
} catch (error) {
if (error instanceof ValidationError) {
console.error("Validation Error:", error.message);
} else {
console.error("Unknown Error:", error);
}
}
Explanation
In this example, the ValidationError
class provides a custom error type. The validateUser
function uses this custom error to provide specific error messages for validation failures.
Conclusion
Error handling in TypeScript extends JavaScript's capabilities with its type system, enabling you to catch errors early and ensure type safety. By understanding and using these error handling techniques, you can write more robust and maintainable code.