28 Aug 2026.

Result Enum alike in Typescript.

Because life is too short to try catch every async function.

Why ?

I got the idea to implement this from the Result Enum from Rust. It allows you properly handle side effects of your code. Typescript has a habit of doing using try/catch but it is heavily associated with "async functions" and not managing side effects.

Implementation Details.

Typescript doesn't work the same way as Rust (unfortunately) so I couldn't make Result here as an Enum or an Interface, I had to utilize classes and type system.

I implemented Result as a type to allow for type narrowing, using it in class to define methods on it. Type narrowing was crucial to avoid accessing error or data without confirming success.

Type Narrowing & ResultType.

Type Narrowing is a method to refine the type of data from a broader one to a specific one.

export type ResultType<T, E> = |
  | { success: true; data: T } 
  | { success: false; error: E };

The result value either being data or error depending on the value of success allows typescript to enforce that success value must be checked before accessing data or error, preventing direct and dangerous access. You can directly wrap this type in a class and define methods to use it in your code.

export class Result<T, E> { 
  public readonly value: ResultType<T, E>; 
  ..... 
}

Important Static Methods.

1. TryCatch
Since Javascript allows you to throw anything and catch block types it as unknown. Result.tryCatch wraps a try those async calls into a clean Result<T, unknown> type.

const data: Result<Response, unknown> = await Result.tryCatch(
  {}, 
  async() => await fetch('https://www.loremus.gay')
)

is equivalent to

async function query(): Promise<Result<Response, unknown>> {
  try {
    const q = await fetch('https://www.loremus.gay');
    return Result.ok(q) 
  } catch(error) {
    return Result.error(error)
  }
}

const data = await query();

2. Fallback
is a method that takes in functions with same arguments and return result. It runs one method after another in the array and returns the first one that succeeds, if all fails, it returns default error.

const data: Result<UserData, string> = Result.fallback(
  {id: "userId"}, 
  "all ways to fetch data failed", 
  [fetchFromCache, fetchFromDb1, fetchFromDb2]
)

3. Settle
is a method that takes in a an array of Promised Results, awaits them and makes sure they are all successful, and returns the successful data as a tuple. If even one of them fails, it returns null.

const data: Result<[string, number], null> = Result.settle([
  returnsStringResultPromise(), 
  returnsNumberResultPromise()
])

Method Chaining.

1. mapOk() = Transform data if Result is successful.
2. mapError() = Transforms the error message if it failed.
3. match() = Takes in 2 function, one runs if failed, another runs if succeeded. Both transform results.
4. onOk() = Runs the function if result is successful but doesn't transform the result.
5. onError() = Runs the function on error but doesn't change the result.

We live in a society.

If you are working with Result and sending data from Next JS Server Actions to ur frontend. Next will give you an error that you can't use classes. To convert the Result<T, E> class back into the type, you should use the method "type()" and it will work.