duhan
Blog

Errors as Values V2

I wrote an article about returning errors as values in Errors as Values in TypeScript last week. While I was scrolling through my Twitter feed, I saw a tweet about the same topic. I read the tweet from @mattpocockuk and I realized that there is a better way to handle errors in TypeScript in terms of readability and maintainability. In this article, I will explain how to return errors as values with helper functions.

The disadvantage in the previous article was that the return type of each function had to be entered manually to capture the types. This is ugly and exhausting to write. Instead, we can write something like this:

const getData = async () => {
    const res = await fetch("some-url.com");
    if (!res.ok) {
        return Failure("Failed to fetch data");
    }

    const data = await res.json();
    return Success<number>(data);
}

const result = await getData();

In this example, we have two helper functions, Success and Failure. These functions are used to create a common return type for all functions that return errors as values. This way, we can easily understand the return type of the function and handle errors in a more readable way. To do this, we need to define the Success and Failure functions as follows:

type Success<T> = {
    success: true;
    value: T;
}

type Failure<T> = {
    success: false;
    error: T;
}

const Success = <T>(value: T): Success<T> => ({
    success: true,
    value,
});

const Failure = <T>(error: T): Failure<T> => ({
    success: false,
    error,
});

Now you can get better TypeScript experience. The return type of the getData() function is automatically becomes const result: Failure<string> | Success<number>. You can use it like below:

// ...

const result = await getData();
if (result.success) {
    console.log(result.value);
} else {
    console.error(result.error);
}