TypeScript Error Handling
Error handling is an important part of ensuring program robustness.
TypeScript provides a powerful type system that can help developers better handle and prevent errors.
This article introduces common error handling patterns and best practices in TypeScript.
SVG Diagram: Error Handling Approaches
Why Good Error Handling Is Needed
Any program may encounter error situations, such as network request failures, missing files, invalid user input, and so on.
Good error handling can prevent program crashes, provide friendly error messages, and help developers locate problems.
TypeScript's type system can find potential problems at compile time, reducing runtime errors.
Concept explanation:There are two main ways to handle errors: exception handling (try-catch) and return value handling (Result type). The former throws exceptions to indicate errors, while the latter carries error information through return values.
Custom Error Types
Create custom error types by extending the Error class, allowing them to carry more error information.
This makes error handling more precise and structured.
Example
// Extend the built-in Error class and add an error code
class AppError extends Error {
// Error code for programmatic error handling
code: string;
// Constructor
constructor(message: string, code: string) {
super(message); // Call the parent class constructor
this.name = "AppError"; // Set the error name
this.code = code; // Save the error code
}
}
// Safe division function
function divide(a: number, b: number): number {
// Check if the divisor is zero
if (b === 0) {
// Throw a custom error
throw new AppError("Cannot divide by zero", "DIVIDE_BY_ZERO");
}
return a / b;
}
// Use try-catch to catch the error
try {
var result = divide(10, 0);
} catch (error) {
// Check the error type
if (error instanceof AppError) {
console.log("Application error: " + error.message + ", code: " + error.code);
} else {
console.log("Unknown error: " + error);
}
}
Output:
应用错误: Cannot divide by zero, 代码: DIVIDE_BY_ZERO
Error code:Adding codes to errors helps the program handle different types of errors more precisely.
Result Type to Avoid Exceptions
Another error handling approach is to use the Result type.
It carries error information through return values instead of throwing exceptions. This approach is common in functional programming.
Example
// On success includes ok: true and a value; on failure includes ok: false and an error
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
// Division function using the Result type
function safeDivide(a: number, b: number): Result<number, string> {
// Check if the divisor is zero
if (b === 0) {
// Return an error result
return { ok: false, error: "Cannot divide by zero" };
}
// Return a success result
return { ok: true, value: a / b };
}
// Call the function and handle the result
var result = safeDivide(10, 2);
// Handle based on the result type
if (result.ok) {
console.log("Result: " + result.value);
} else {
console.log("Error: " + result.error);
}
Output:
结果: 5
Advantages:The Result type makes error handling explicit; callers must handle possible errors and cannot ignore them.
Async Function Error Handling
In async functions, error handling is especially important.
You can use try-catch or the Result type to handle errors in asynchronous operations.
Example
interface User {
id: number;
name: string;
}
// Simulate an async function to fetch a user
async function fetchUser(id: number): Promise<Result<User, Error>> {
try {
// Simulate a network request
var response = await fetch("/api/users/" + id);
var user = await response.json();
// Return a success result
return { ok: true, value: user };
} catch (error) {
// Return an error result
return { ok: false, error: error as Error };
}
}
// Main function
async function main() {
// Call the async function
var result = await fetchUser(1);
// Handle the result
if (result.ok) {
console.log("User: " + JSON.stringify(result.value));
} else {
console.log("Error: " + result.error.message);
}
}
// Execute the main function
main();
Output:
User: {"id":1,"name":"Alice"}
Tip:The try-catch in an async function catches any error thrown by await expressions.
General Error Handling Wrapper
You can create a general error handling function to simplify error handling in asynchronous code.
Example
// Accept an async function and return a Result type
async function withErrorHandling<T>(
fn: () => Promise<T>
): Promise<Result<T, Error>> {
try {
// Execute the passed-in async function
var data = await fn();
// Return a success result
return { ok: true, value: data };
} catch (error) {
// Return an error result
return { ok: false, error: error as Error };
}
}
// Use general error handling
// Simulate fetching data
var result = await withErrorHandling(async function() {
var response = await fetch("/api/data");
return response.json();
});
// Handle based on the result
if (result.ok) {
console.log("Data: " + JSON.stringify(result.value));
} else {
console.error("Error:", result.error);
}
Best practice:Wrapping general error handling logic reduces code duplication and improves code maintainability.
Notes
- Don't ignore errors:Do not use empty catch blocks to catch and ignore errors
- Specify error types clearly:Use specific error types whenever possible, rather than the generic Error
- Error boundaries:Establish a unified error handling mechanism in the application
- Don't overuse exceptions:For predictable errors, prefer return values over throwing exceptions
Suggestion:Choose an error handling approach based on the scenario: use exceptions for program errors, and Result for business errors.
Summary
Good error handling is the foundation for building robust applications.
- Custom errors:Extend the Error class and add information such as error codes
- Result type:Handle errors through return values, avoiding exceptions
- async/await:Use try-catch for asynchronous errors
- Error wrapping:Create general error handling functions
- Error boundaries:Establish a unified error handling mechanism
Best practice:Choose an appropriate error handling approach based on the specific scenario, balancing code readability and robustness.
Other Extensions