DEV Community

Cover image for Go like error handling in TypeScript
Karan Pratap Singh
Karan Pratap Singh

Posted on

Go like error handling in TypeScript

In this article we'll learn about how we can handle our errors like Go with TypeScript.

Note: In TypeScript this is probably not a "best practice", or even a good practice at all but let's have fun experimenting nonetheless!

Let's take the following as an example.

import * as fs from 'fs/promises';

async function main(): Promise<void> {
  try {
    const result: Buffer = await fs.readFile('./package.json');
    // Do something with result
  } catch (error) {
    // Handle error
  }
}

main();
Enter fullscreen mode Exit fullscreen mode

In Go, this should look as below.

package main

import "io/ioutil"

func main() {
    data, err := ioutil.ReadFile("./package.json")

    if err != nil {
        // Handle error
    }

    // Do something with data
}
Enter fullscreen mode Exit fullscreen mode

Let's write our async handler helper function! This function basically returns a Tuple of result and error as TypeScript doesn't have multiple returns.

type Maybe<T> = T | null;

type AsyncResult = any;
type AsyncError = any;
type AsyncReturn<R, E> = [Maybe<R>, Maybe<E>];
type AsyncFn = Promise<AsyncResult>;

async function async<R = AsyncResult, E = AsyncError>(
  fn: AsyncFn
): Promise<AsyncReturn<R, E>> {
  try {
    const data: R = await fn;
    return [data, null];
  } catch (error) {
    return [null, error];
  }
}
Enter fullscreen mode Exit fullscreen mode

We can use our async utility as below. We can pass our Result and Error generics types.

import * as fs from 'fs/promises';

async function main(): Promise<void> {
  const fn: Promise<Buffer> = fs.readFile('./package.json');
  const [data, error] = await async<Buffer, NodeJS.ErrnoException>(fn);

  if (error) {
    // Handle error
  }

  // Do something with data
}

main();
Enter fullscreen mode Exit fullscreen mode

Perfect! We now have isolated our try/catch with Go like error handling pattern.

Discussion (3)

Collapse
lukeshiru profile image
LUKESHIRU • Edited

You can achieve something quite simpler without using async/await and Maybe, something like:

const async = <Result>(promise: Promise<Result>) =>
    promise.then(Array.of).catch(error => [, error]) as Promise<
        [result?: Result, error?: unknown]
    >;
Enter fullscreen mode Exit fullscreen mode

And then you can use it like this:

import * as fs from "node:fs/promises";

const main = () =>
    async(fs.readFile("./package.json")).then(([result, error]) => {
        if (!error) {
            console.log(result);
        }
    });

main();
Enter fullscreen mode Exit fullscreen mode

As you mentioned, this is not the best (you can just use Promises normally), but it is a fun "experiment".

Cheers!

Collapse
foresthoffman profile image
Forest Hoffman

That's a fun exercise! XD

Collapse
gaurav5430 profile image
Gaurav Gupta

this is a legit pattern, we use this (without typescript) in our codebase to get rid of try catch at the calling site