Welcome back to the final post in the series "Level up your Typescript game, functionally".
In the previous post, we looked at some functional concepts (like Monads) from the Scala
programming language and re-implemented those concepts in Typescript with the Option
and Either
types.
Now let's look at another useful type from the Rust
programming language.
The Result
type
The extremely type safe Rust
language exposes a type called Result
that is very similar to the Either
type (from Scala/Haskell) but it is exclusively used to model the result of an operation that can either be a Success or Failure (error).
This is what it looks like [ Source ]
pub enum Result<T, E> {
Ok(T),
Err(E),
}
Example usage
let op: Result<i32, &str> = Ok(42);
assert_eq!(x.is_ok(), true);
let x: Result<i32, &str> = Err("Some error message");
assert_eq!(x.is_ok(), false);
We can implement the Result
type in Typescript too. Let's modify our Either
implementation from our previous post and give it a go!
type Ok<T> = { value: T }
type Err<E> = { error: E }
// The Result Type
type Result<T, E = Error> = Ok<T> | Err<E>
And the util functions to wrap objects
export const ok = <T>(value: T): Ok<T> => ({ value })
export const err = <E>(error: E): Err<E> => ({ error })
And finally our matchers interface and match function
interface Matchers<T, E extends Error, R1, R2> {
ok(value: T): R1
err(error: E): R2
}
const match =
<T, E extends Error, R1, R2>(matchers: Matchers<T, E, R1, R2>) =>
(result: Result<T, E>) =>
'value' in result
? matchers.ok(result.value)
: matchers.err(result.error)
As you can see this is very similar to the Either
type implementation but uses Ok
and Error
instead of Left
and Right
.
And a practical example from before as follows
const getDataFromDB =
async (ctx: Context): Promise<Result<Data>> => {
try {
const result = await ctx.db.primary.getOne()
return ok(result)
} catch (err) {
console.error('Error getting data from DB', err)
return err(err)
}
}
// using pipe and andThen from the NodeJS 'ramda' package
const app = async () => {
return pipe(
getDataFromDB(ctx)
andThen(
match({
err: (err) => throw err,
ok: (value) => return value
})
)
}
Summary
Through this entire series of posts, we saw that we can use pure functional concepts to re-write our Typescript application flows using appropriate types and make our code functionally aware thereby improving dev efficiency and utility especially when it comes to handling domain errors instead of throwing exceptions everywhere.
Takeaways and Resources
We learned 4 new functional types to help optimize our code and flows. They are namely ResultTuple
, Option
, Either
and Result
.
If you want to use any of these types mentioned in this series, check out this handy ts-utils
NodeJS package that I created that encompasses these types as exports so you can use whatever variation makes sense for your use case. To install it simply run this command
npm i -S @universal-apps/ts-utils
The advantage of the types we have discussed in detail in this series is that you can use as much or as little as your want and swap out portions of your codebase without affecting others.
However if you want to use a full fledged functional library with lots of utilities, I recommend checking out fp-ts and this handy cookbook that teaches you how to use it with examples. A newer library called EffectTS that also uses similar concepts and patterns has also been recently gaining popularity if you want to check that out.
Other libraries to check out are pratica and ramda
Last but not least if you want a good learning resource on Functional Programming, check out this series on Youtube.
Acknowledgements
I was heavily inspired by this post from Dan Imhoff on using Results in Typescript for this entire series of posts and extending the concepts.
Conclusion
Congrats on making it to the end of this final post in the series! You have leveled up once more 🍄🍄🍄 and completed your quest. Thanks for going on this journey with me.
If you found this post and/or series helpful, please upvote/react to it and share your thoughts and feedback in the comments.
Onwards and upwards 🚀
Top comments (0)