JSPM

  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • 0
  • Score
    100M100P100Q43495F
  • License MIT

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

Package Exports

    This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (go-errors) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

    Readme

    go-errors

    npm version TypeScript License: MIT

    A TypeScript library that brings Go-style error handling to JavaScript/TypeScript. Handle errors elegantly without try-catch blocks, using a functional approach that's both type-safe and intuitive.

    Features

    • 🎯 Type-safe error handling with full TypeScript support
    • 🔄 Go-style result tuples [value, error]
    • Zero dependencies and lightweight
    • 🔀 Unified API for both synchronous and asynchronous operations
    • 🛡️ Predictable error handling without try-catch blocks
    • 📦 Tree-shakeable and optimized for bundle size

    Installation

    npm install go-errors
    # or
    yarn add go-errors
    # or
    pnpm add go-errors

    Quick Start

    import { go } from 'go-errors';
    
    // Synchronous usage
    const [value, error] = go(() => {
      // Your code that might throw
      return "success";
    });
    
    if (error) {
      console.error("Something went wrong:", error.message);
    } else {
      console.log("Got value:", value);
    }
    
    // Asynchronous usage
    const [data, err] = await go(fetch("https://api.example.com/data"));
    if (err) {
      console.error("API call failed:", err.message);
    } else {
      console.log("API data:", data);
    }

    Usage Examples

    Basic Error Handling

    import { go } from 'go-errors';
    
    function divide(a: number, b: number): number {
      if (b === 0) throw new Error("Division by zero");
      return a / b;
    }
    
    // Synchronous error handling
    const [result, err] = go(() => divide(10, 0));
    if (err) {
      console.log("Failed to divide:", err.message); // "Failed to divide: Division by zero"
    } else {
      console.log("Result:", result);
    }

    Async/Promise Handling

    import { go } from 'go-errors';
    
    async function fetchUserData(id: string) {
      const [response, fetchError] = await go(
        fetch(`https://api.example.com/users/${id}`)
      );
      
      if (fetchError) {
        return [null, fetchError] as const;
      }
      
      const [data, parseError] = await go(response.json());
      if (parseError) {
        return [null, parseError] as const;
      }
      
      return [data, null] as const;
    }
    
    // Usage
    async function main() {
      const [userData, error] = await fetchUserData("123");
      if (error) {
        console.error("Failed to fetch user:", error.message);
        return;
      }
      console.log("User data:", userData);
    }

    Custom Error Types

    import { go } from 'go-errors';
    
    class ValidationError extends Error {
      constructor(message: string) {
        super(message);
        this.name = 'ValidationError';
      }
    }
    
    function validateUser(user: unknown) {
      if (typeof user !== 'object' || !user) {
        throw new ValidationError('Invalid user object');
      }
      return user;
    }
    
    const [user, error] = go<unknown, ValidationError>(() => 
      validateUser({ name: 'John' })
    );
    
    if (error) {
      if (error instanceof ValidationError) {
        console.log("Validation failed:", error.message);
      } else {
        console.log("Unknown error:", error.message);
      }
    }

    Working with Multiple Operations

    import { go } from 'go-errors';
    
    async function processUserData(userId: string) {
      // Fetch user
      const [user, userError] = await go(fetchUser(userId));
      if (userError) return [null, userError] as const;
    
      // Fetch user's posts
      const [posts, postsError] = await go(fetchUserPosts(userId));
      if (postsError) return [null, postsError] as const;
    
      // Process everything
      return [{
        user,
        posts,
        timestamp: new Date()
      }, null] as const;
    }

    Edge Cases

    The library handles various edge cases gracefully:

    1. Circular References: Objects with circular references are handled gracefully with descriptive error messages:

      const circular = { foo: 'bar' };
      circular.self = circular;
      const [result, err] = go(() => circular);
      // err.message will contain information about the circular structure
    2. Special Objects: Special JavaScript objects are properly stringified:

      // RegExp
      const [_, regexErr] = go(() => { throw /test/gi; });
      console.log(regexErr.message); // "/test/gi"
      
      // Date
      const [_, dateErr] = go(() => { throw new Date(); });
      // Preserves date format in error message
    3. Falsy Values: Properly handles falsy values (undefined, null, 0, false, ''):

      const [result, err] = go(() => false);
      console.log(result === false); // true
      console.log(err === null); // true

    API Reference

    Core Functions

    go<T, E = Error>(fn: () => T): Result<T, E>

    go<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>

    The main function that handles both synchronous and asynchronous operations.

    // Sync
    const [value, error] = go(() => someOperation());
    
    // Async
    const [value, error] = await go(somePromise);

    goSync<T, E = Error>(fn: () => T): Result<T, E>

    Specifically handles synchronous operations.

    const [value, error] = goSync(() => {
      // synchronous code that might throw
      return someValue;
    });

    goAsync<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>

    Specifically handles asynchronous operations.

    const [value, error] = await goAsync(fetch('https://api.example.com'));

    Types

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

    Best Practices

    1. Type Your Errors: Always specify error types for better type safety:

      const [value, error] = go<number, ValidationError>(() => validate(input));
    2. Early Returns: Use early returns with error checking:

      const [data, error] = await fetchData();
      if (error) return [null, error] as const;
    3. Error Propagation: Propagate errors up the call stack:

      function processData() {
        const [data, error] = getData();
        if (error) return [null, error] as const;
        // process data
      }

    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

    This project is licensed under the MIT License - see the LICENSE file for details.

    Acknowledgments

    • Inspired by Go's error handling pattern
    • Built with TypeScript for type safety
    • Designed for modern JavaScript/TypeScript applications

    Made with ❤️ for the TypeScript community