Introduction
There are two main methods of handling errors, either using a Result
pattern, or by throwing (and therefore handling) exceptions. The Result
pattern is more common in functional programming, since functional programming avoids side-effects when possible.
The main benefit behind a Result
object, or an error as a value is pretty self-explanatory. You can explicitly handle the possible error from Result
, and you can see that within the type definition of the function.
This isn’t only an issue in TypeScript, but it’s the language I work with the most, so I’ll be writing the rest of this using it.
Exceptions
Whenever a function signature looks like the following, you can’t tell whether it’s going to throw or not:
This logic gets even worse when trying to handle the errors, especially with TypeScript. This is mainly due to the function’s error not being a type, resulting in the error produced from the function being typed as unknown
whenever you try to handle the error.
Additionally, if you want to define the variable outside of the try
/catch
, you have to create a mutable let
, and it’s just a massive headache:
Results
The great thing about the Result
pattern is that errors are explicitly known. For example, if you had the same divide
function as earlier, the return type would be something like Result<number, string>
, where string
is the error type.
You can create a very basic Result
type in TypeScript using a union type:
The divide
function, with the Result
type would look something like this:
Not only is this function a lot more clean (at least in my opinion, some people probably wouldn’t like the ternary operator), but it’s explicit in the possibility of an error. For example, whenever you handle the function:
TypeScript makes sure you explicitly check for the ok
field within the Result
object before you can do anything with the function output. This is especially helpful to stop your code from randomly crashing without you even knowing that a function could possibly crash.
Summary
The Result
pattern is definitely exciting, and concepts from functional programming are incredibly important. For example, Rust took heavy inspiration from many functional programming languages with their implementation of Result
as a standard within their language.
Quote
“Good code is its own best documentation.” — Steve McConnell