Skip to content

Instantly share code, notes, and snippets.

@joggienl
Last active July 10, 2025 08:53
Show Gist options
  • Save joggienl/675f97011c99248d479845a1709b72d8 to your computer and use it in GitHub Desktop.
Save joggienl/675f97011c99248d479845a1709b72d8 to your computer and use it in GitHub Desktop.
🎯 TypeScript Promise Error Handling Utility
/**
* tryCatch - Go-style error handling for JavaScript Promises
*
* A utility function that bridges Promise-based and tuple-based error handling.
* Perfect for avoiding try-catch blocks while maintaining explicit error handling.
*
* @author Jogchum Koerts
* @version 2.0.0
* @created 2025-07-09
* @license MIT
*
* Usage:
* 1. Copy this file to your project's utils folder
* 2. Import: import { tryCatch } from './utils/tryCatch'
* 3. Use: const [data, error] = await tryCatch(somePromise)
*
* Linter Note:
* Some linters may suggest converting this to an async function. We intentionally use
* .then()/.catch() here for semantic consistency - this utility wraps Promises, so using Promise
* methods feels more natural than async/await syntax.
*
* File-level disable examples:
* - ESLint: `// eslint-disable @typescript-eslint/promise-function-async`
* - Biome: `// biome-ignore lint/suspicious/noAsyncPromiseExecutor: semantic consistency`
*
* GitHub Gist: https://gist.github.com/joggienl/675f97011c99248d479845a1709b72d8
*/
// You can add the linter disable line here if you need...
/**
* Handles and catches specific types of errors from a promise, returning a tuple-based result.
*
* This function bridges the gap between JavaScript's Promise-based error handling and
* tuple-based error handling patterns (similar to Go's `result, err` approach). It follows
* the data-first convention established by JavaScript Promises, where success values take
* precedence over error handling.
*
* @param promise A promise that resolves to a value of type T.
* @param errorsToCatch An optional array of error constructors to catch. If not provided, catches
* all errors. If provided, only catches errors that are instances of the
* specified error classes.
* @returns A promise that resolves to either:
* - A 2-element tuple `[T, null]` in case of success, where T is the resolved value
* - A 2-element tuple `[null, InstanceType<E>]` in case of error, where E is the error type
* @throws If `errorsToCatch` is provided and the error is not an instance of any specified error
* class, the original error is re-thrown.
*
* @example
* ```typescript
* // Basic usage - catches all errors
* const [data, error] = await tryCatch(fetchUserData());
* if (error) {
* console.error('Failed to fetch user:', error);
* return;
* }
* console.log('User data:', data);
* ```
*
* @example
* ```typescript
* // Catch specific error types only
* const [result, error] = await tryCatch(
* riskyOperation(),
* [ValidationError, NetworkError]
* );
* if (error) {
* // Handle only ValidationError or NetworkError
* handleKnownError(error);
* }
* // Other errors will be thrown normally
* ```
*
* @example
* ```typescript
* // Functional error handling pattern
* const [users, fetchError] = await tryCatch(api.getUsers());
* const [posts, postError] = await tryCatch(api.getPosts());
*
* if (fetchError || postError) {
* return handleErrors({ fetchError, postError });
* }
*
* return { users, posts };
* ```
*/
function tryCatch<T, E extends new (message?: string) => Error>(
promise: Promise<T>,
errorsToCatch?: E[],
): Promise<[T, null] | [null, InstanceType<E>]> {
return promise
.then(data => [data, null] as [T, null])
.catch(error => {
if (errorsToCatch == undefined) {
return [null, error]
}
if (errorsToCatch.some(errorClass => error instanceof errorClass)) {
return [null, error]
}
throw error
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment