DEV Community

loading...
Cover image for Go like error handling in TypeScript

Go like error handling in TypeScript

Karan Pratap Singh
Software Engineer & Solutions Architect
・1 min read

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