Errors as Values
Errors are inevitable in the software development process. Traditionally, try-catch
blocks are used to manage errors. However, there are alternative methods, such as the errors as values approach, especially in languages with strong type systems like TypeScript. In this article, we will discuss what returning errors as values is, the advantages of this approach, and in which cases it should be preferred.
What is Errors as Values?
Returning errors as a value means that instead of throwing an exception
, a function returns a value indicating the error. This value indicates what the error was, or whether the operation was successful. In TypeScript, this approach is implemented by including error conditions in the return types of functions.
For example, we can return the result of a paginated API call as follows:
type PaginatedRes<T> =
| {
success: true;
data: T[];
totalPages: number;
}
| {
success: false;
};
In this example, if the API call is successful, success: true, data and totalPages are returned. Otherwise success: false is returned. This is a good example of the “errors as values” approach, as we return error conditions as a value instead of discarding them.
Advantages of Errors as Values
Type-Safety
TypeScript allows you to explicitly specify which errors can occur if errors are returned as a value. This way, error conditions can be detected at the build time of the code and incorrect error handling practices can be avoided.
function getPaginatedData<T>(page: number): PaginatedRes<T> {
if (page < 1) return { success: false };
// happy path
return {
success: true,
data: [],
totalPages: 10
};
}
const result = getPaginatedData<User>(1);
This way, you can type-safe check whether the value returned when you call the getPaginatedData()
function succeeds or fails.
Readability
When errors are returned as values, the code becomes more readable. Instead of having to read the entire code to understand how errors are handled, you can easily see the error conditions by looking at the return type of the function.
const result = getPaginatedData(2);
if (result.success) console.log(result.data);
else console.log('Something went wrong');
Compared to the try-catch
block, the code is more readable and easier to understand.
You don’t have to use the errors as values approach in every situation. However, it is a good practice to use this approach in TypeScript. This way, you can handle errors more effectively and avoid unexpected errors in your code.