npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

go-errors

v2.0.0

Published

Go-style error handling for TypeScript with full type safety, async, and fetch support

Readme

go-errors

A lightweight, type-safe TypeScript library that brings Go-style error handling to JavaScript/TypeScript. Say goodbye to try-catch blocks and hello to elegant, functional error handling! This library brings the simplicity and elegance of Go's error handling pattern to your TypeScript/JavaScript projects.

Features

  • 🎯 Type-safe: Full TypeScript support with precise type inference
  • 🔄 Unified API: Same pattern for both sync and async operations
  • 🌐 Fetch Support: Built-in wrapper for fetch operations with transformers
  • 🪶 Lightweight: Zero dependencies, minimal overhead
  • 🔒 Immutable: Results are readonly tuples
  • 🎨 Flexible: Support for custom error types
  • 📦 Tree-shakeable: Only import what you need
  • 🔍 Predictable: No more try-catch spaghetti code
  • 🚀 Fast: Minimal runtime overhead
  • 💡 Intuitive: Familiar pattern for Go developers

Installation

bun add go-errors

Quick Start

import { go } from 'go-errors';

// Synchronous usage
let [value, err] = go(() => {
  if (Math.random() > 0.5) throw new Error('Bad luck!');
  return 42;
});

if (err) {
  console.error('Something went wrong:', err);
} else {
  console.log('Got value:', value);
}

// Asynchronous usage
let [data, err] = await go(fetch('https://api.example.com/data'));

if (err) {
  console.error('Failed to fetch:', err);
} else {
  console.log('Got data:', data);
}

Core Concepts

The Result Type

The library uses a tuple-based Result type that's similar to Go's multiple return values:

type Result<T, E = Error> = readonly [T, null] | readonly [null, E];

This means a function will always return either:

  • [value, null] for success
  • [null, error] for failure

Variable Declaration Best Practice

Following Go's convention, we recommend using let instead of const for result declarations. This allows you to reuse the error variable name (typically err) throughout your code, just like in Go:

// ✅ Recommended: Using let
let [value, err] = go(() => someOperation());
if (err) return handleError(err);

// Another operation using the same err variable
let [result, err] = go(() => anotherOperation());
if (err) return handleError(err);

// ❌ Not recommended: Using const requires unique variable names
const [value1, error1] = go(() => someOperation());
if (error1) return handleError(error1);

const [value2, error2] = go(() => anotherOperation());
if (error2) return handleError(error2);

Main Functions

1. go<T, E = Error>

The main function that handles both synchronous and asynchronous operations:

// Synchronous
let [value, err] = go(() => someOperation());

// Asynchronous
let [value, err] = await go(somePromise);

// With custom error types
let [value, err] = go<number, CustomError>(() => validate(input));

2. goFetch<T, E = string>

A powerful fetch wrapper with built-in error handling and transformation capabilities:

interface User {
  id: number;
  name: string;
}

// Basic usage
let [user, err] = await goFetch<User>('https://api.example.com/user/1');

// Advanced usage with options
let [user, err] = await goFetch<User>('https://api.example.com/user/1', {
  // Request options (extends Fetch API options)
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({ name: 'John' }),
  
  // Response transformation
  responseTransformer: (data: unknown) => ({
    ...data as User,
    lastFetched: new Date()
  }),
  
  // Error transformation
  errorTransformer: (error: unknown) => {
    if (error instanceof Error) {
      return `API Error: ${error.message}`;
    }
    return 'Unknown error occurred';
  }
});
goFetch Options

The goFetch function accepts an optional options object that extends the standard Fetch API options:

interface GoFetchOptions<T = any, E = string> extends RequestInit {
  // Transform the response data before returning
  responseTransformer?: (data: unknown) => T;
  
  // Transform errors into your preferred format
  errorTransformer?: (error: unknown) => E;
  
  // All standard fetch options are supported
  method?: string;
  headers?: HeadersInit;
  body?: BodyInit;
  mode?: RequestMode;
  credentials?: RequestCredentials;
  cache?: RequestCache;
  // ... and more
}

Advanced Usage

Custom Error Types

You can specify custom error types for more precise error handling:

class ValidationError extends Error {
  constructor(public field: string, message: string) {
    super(message);
  }
}

let [value, err] = go<number, ValidationError>(() => {
  if (input < 0) throw new ValidationError('input', 'Must be positive');
  return input * 2;
});

if (err) {
  console.error(`Validation failed for ${err.field}: ${err.message}`);
}

Complex API Interactions

interface ApiResponse<T> {
  data: T;
  status: string;
  metadata: {
    timestamp: string;
    version: string;
  };
}

interface ApiError {
  code: number;
  message: string;
  details?: string;
}

interface User {
  id: number;
  name: string;
  email: string;
}

async function fetchUserData(userId: string) {
  let [user, err] = await goFetch<ApiResponse<User>, ApiError>(
    `https://api.example.com/user/${userId}`,
    {
      headers: {
        'Authorization': 'Bearer token',
      },
      responseTransformer: (data: unknown) => {
        const response = data as ApiResponse<User>;
        // Add custom validation
        if (!response.data.id) {
          throw new Error('Invalid user data');
        }
        return response;
      },
      errorTransformer: (error: unknown) => {
        if (error instanceof Error) {
          return {
            code: 500,
            message: error.message,
            details: error.stack
          };
        }
        return {
          code: 400,
          message: 'Unknown error'
        };
      },
    }
  );

  if (err) {
    console.error(`API Error ${err.code}: ${err.message}`);
    return [null, err] as const;
  }

  return [user.data, null] as const;
}

Error Handling Patterns

Sequential Operations

async function processUserData(userId: string) {
  // Fetch user
  let [user, err] = await goFetch<User>(`/api/users/${userId}`);
  if (err) return [null, err] as const;

  // Fetch user's posts
  let [posts, err] = await goFetch<Post[]>(`/api/users/${userId}/posts`);
  if (err) return [null, err] as const;

  // Process everything
  let [result, err] = go(() => ({
    user,
    posts,
    timestamp: new Date()
  }));
  
  return [result, err] as const;
}

Parallel Operations

async function fetchUserDashboard(userId: string) {
  // Fetch multiple resources in parallel
  let [results, err] = await go(Promise.all([
    goFetch<User>(`/api/users/${userId}`),
    goFetch<Post[]>(`/api/users/${userId}/posts`),
    goFetch<Activity[]>(`/api/users/${userId}/activity`)
  ]));

  if (err) return [null, err] as const;

  const [[user, userErr], [posts, postsErr], [activity, activityErr]] = results;

  // Check for individual errors
  if (userErr || postsErr || activityErr) {
    return [null, new Error('Failed to fetch some dashboard data')] as const;
  }

  return [{
    user,
    posts,
    activity,
    lastUpdated: new Date()
  }, null] as const;
}

Best Practices

  1. Always Check for Errors First

    let [data, err] = await go(fetchData());
    if (err) {
      // Handle error first
      return handleError(err);
    }
    // Then work with data
    processData(data);
  2. Use Type Parameters for Better Type Safety

    let [value, err] = go<number, CustomError>(() => validate(input));
  3. Avoid Nested Error Handling

    // Good
    let [data, err] = await go(step1());
    if (err) return handleError(err);
       
    let [result, err] = await go(step2(data));
    if (err) return handleError(err);
       
    // Not recommended
    let [data, err] = await go(step1());
    if (!err) {
      let [result, err] = await go(step2(data));
      if (!err) {
        // ...
      }
    }
  4. Use Consistent Error Types

    // Define your error types
    type AppError = ValidationError | DatabaseError | NetworkError;
    
    // Use them consistently
    let [data, err] = go<Data, AppError>(() => processData());
  5. Propagate Errors Up

    function processData(): GoResult<ProcessedData, AppError> {
      let [data, err] = getData();
      if (err) return [null, err] as const;
    
      let [processed, err] = go(() => transform(data));
      if (err) return [null, err] as const;
    
      return [processed, null] as const;
    }

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/AmazingFeature)
  3. Commit your changes (git commit -m 'Add some AmazingFeature')
  4. Push to the branch (git push origin feature/AmazingFeature)
  5. Open a Pull Request

License

MIT License - see the LICENSE file for details.

Credits

Created by Ashkan Samadiyan


Made with ❤️ for the TypeScript community