Error handling method in TypeScript with Result type
When it comes to error handling in TypeScript, throwing errors is not the best approach, especially where errors are expected. Let’s explore a better alternative.
Many libraries are out there, inspired by Go/Rust style of error handling. They use a pattern of returning an object with either a success or error state. But if you prefer a simpler method, you can create your own type to handle errors in a more elegant way.
Implementation #
export type ErrorMessage = {
code?: string;
traces?: unknown[];
};
type Data<T> = {
data: T;
error?: never;
};
type Error = {
data?: never;
error: ErrorMessage;
};
export type Result<T> = NonNullable<Data<T> | Error>;
The main star here is the Result
type. Since it’s a NonNullable
, we can be sure that every time, we will have either a data
or an error
property. This way, we can easily check if the result is an error or not.
Usage #
Wherever you expect errors to happen, you can use this type. For example, in a network request:
import { Result } from './types';
export const ERRORS = {
FETCH_UNAUTHORIZED: 'FETCH_UNAUTHORIZED',
FETCH_FAILED: 'FETCH_FAILED',
}
export fetchData = async (): Result<ImportantData> => {
const session = await getSession();
const token = session?.accessToken;
if (!token) {
return { error: { code: ERRORS.FETCH_UNAUTHORIZED } };
}
const response = await fetch('https://api.example.com/data', {
headers: {
Authorization: `Bearer ${token}`,
},
});
const data = await response.json();
if (!response.ok) {
return { error: { code: ERRORS.FETCH_FAILED, traces: [data] } };
}
return { data as ImportantData };
}
And from the presentation layer, we can easily check if the result is an error or not. Let’s say we are using React:
import { useEffect, useState } from "react";
import { fetchData, ERRORS } from "./api";
export const Component = async () => {
const result = await fetchData();
if (result.data) {
return <div>Data: {JSON.stringify(result.data)}</div>;
}
if (result.error.code === ERRORS.FETCH_UNAUTHORIZED) {
return <div>You are not authorized</div>;
} else {
return <div>Unexpected error</div>;
}
};