DEV Community

Cover image for Created Result Type in TypeScript
hirohata
hirohata

Posted on • Edited on

Created Result Type in TypeScript

I created my result type. Repository / NPM

Motivation

Handling errors is a crucial aspect of creating reliable and readable code. While TypeScript (JavaScript) offers a try-catch syntax, it can be challenging to clearly recognize whether a function returns an error or not.
By using the Result type, we can explicitly express that a function has the potential to return an error.

In a recent personal project I joined, we opted for TypeScript. Consequently, we chose to implement and utilize the following Result type:

type Result<T, E> = {
    ok: true,
    value: V
} | {
    ok: false,
    error: E
}

const Ok = <T, E>(value: V): Result<T, E> => ({ ok: true, value });
const Err = <T, E>(error: E): Result<T, E> => ({ ok: false, error });
Enter fullscreen mode Exit fullscreen mode

It works well in the code. We can explicitly express that a function may return an error state, allowing us to handle errors without using try-catch.


const result: Result<T, E> = someFunction();


if (result.ok) {
    // this result is inferred to the OK.
    // do something
} else {
    // this result is error.
    // do error handling.
}


Enter fullscreen mode Exit fullscreen mode

However, this result type resulted in pain in testing.

When writing tests, there are instances where we need to create dummy objects convincing an 'OK' type. In such cases, we found ourselves needing to check if the result is 'OK' or 'Err' using an if-condition. If we attempt to access the value without checking the 'ok' field in the result type, the IDE raises a warning. This can be quite bothersome and affects code readability.


let okValue;

const result = someFunction();

if (result.ok) {
    okValue = result.value;
} else {
    // this is used in tests, so we don't need error handling.
    throw new Error("error message")
}

Enter fullscreen mode Exit fullscreen mode

Created Result Type

To address this situation, I created my result type.


export type Result<T, E> = IOk<T> | IErr<E>;

interface IOk<T> {
    ok: true;
    value: T;
    unwrap(): T;
    unwrapOrElse(defaultValue: T): T;
    unwrapError(): never;
}

interface IErr<E> {
    ok: false;
    error: E;
    unwrap(): never;
    unwrapOrElse(DefaultValue: any): any;
    unwrapError(): E;
}

Enter fullscreen mode Exit fullscreen mode

This key feature of this result type is an implementation of the unwrap method, inspired by Rust's approach. With this method, there is no longer a need to explicitly check the 'ok' field.


const okValue = someFunction().unwrap();

Enter fullscreen mode Exit fullscreen mode

This result type significantly streamlines our development process.

Conclusion

The use of result type in TypeScript is a subject of much debate, and various packages have been created. In our specific use case, the result type I created meets our requirements satisfactorily at least.

I want to use Rust. 🦀🦀🦀

Top comments (0)