go Function
Detailed documentation for the go function in go-errors.
go
Function
The go
function is designed for handling asynchronous operations (Promises) that might reject, providing a Go-style error handling approach in TypeScript and JavaScript.
Type Signature
function go<T, E = Error>(promise: Promise<T>): Promise<Result<T, E>>;
Type Parameters:
T
: The type of the successful value (the type that the Promise resolves to).E
: The type of the error. Defaults toError
if not specified.
Parameters:
promise
: APromise<T>
that might resolve to a value of typeT
or reject with an error.
Returns:
Promise<Result<T, E>>
: A Promise that always resolves to aResult<T, E>
tuple. This tuple will be:[value, null]
if the inputpromise
resolves successfully.[null, error]
if the inputpromise
rejects.
Basic Usage
import { go } from 'go-errors';
async function fetchData(): Promise<string> {
const response = await fetch('/api/data');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.text();
}
async function main() {
let [data, err] = await go(fetchData());
if (err) {
console.error("Data fetching failed:", err.message);
} else {
console.log("Fetched data:", data);
}
}
main();
Explanation:
fetchData()
is an asynchronous function that fetches data from an API. It might resolve with the data or reject with an error.go(fetchData())
wraps thefetchData()
Promise. Thego
function never rejects. Instead, it always resolves to aResult
tuple.let [data, err] = await go(fetchData());
This line uses destructuring to get thedata
anderr
values from theResult
tuple.- If
fetchData()
resolves successfully,data
will contain the fetched data, anderr
will benull
. - If
fetchData()
rejects,data
will benull
, anderr
will contain the error.
- If
- The
if (err)
block checks for errors and handles them appropriately.
Custom Error Types
You can use custom error types with go
to improve type safety and provide more context about errors.
import { go } from 'go-errors';
class ApiError extends Error {
constructor(message: string, public status: number) {
super(message);
this.name = 'ApiError';
}
}
async function fetchData(): Promise<string> {
const response = await fetch('/api/data');
if (!response.ok) {
throw new ApiError(`HTTP error! status: ${response.status}`, response.status);
}
return response.text();
}
async function main() {
let [data, err] = await go<string, ApiError>(fetchData()); // Specify the error type
if (err) {
if (err instanceof ApiError) {
console.error("API Error:", err.message, "Status:", err.status);
} else {
console.error("Unexpected Error:", err); // This should ideally not happen
}
} else {
console.log("Fetched data:", data);
}
}
main();
Key Improvements:
- Type Parameter: We use
go<string, ApiError>
to specify that the successful value will be astring
and the error will be anApiError
. instanceof
Check: We useinstanceof ApiError
to check if the error is an instance of our custom error class. This allows us to handleApiError
s specifically.- Error Context: The
ApiError
class includes astatus
property, providing additional context about the error.
go
calls)
Error Propagation (Chaining go
makes it easy to chain asynchronous operations and propagate errors:
async function processData() {
let [data, fetchErr] = await go(fetchData());
if (fetchErr) return [null, fetchErr] as const; // Early return on error
let [processedData, processErr] = await go(process(data));
if (processErr) return [null, processErr] as const; // Early return
return [processedData, null] as const; // Return the final result
}
async function main() {
let [result, err] = await processData();
if(err) {
console.error("Processing failed:", err);
} else {
console.log("Processing successful:", result);
}
}
Key Points:
- Early Returns: Use
if (err) return [null, err] as const;
to immediately return from a function if an error occurs. This avoids nestedif-else
blocks and makes the code cleaner. as const
: Theas const
assertion is crucial here. It tells TypeScript that the returned tuple isreadonly
and has the exact types[null, Error]
or[T, null]
, rather than the more general(T | null)[]
. This ensures type safety when destructuring the result.
Best Practices
- Always Use
let
: Uselet
forResult
tuple declarations. - Explicit Type Parameters: Specify type parameters (
<T, E>
) for better type safety. - Early Returns: Use early returns to handle errors as soon as they occur.
as const
: Useas const
when returningResult
tuples.- Custom Error Types: Define and use custom error types for better error handling and context.
- Consider
goFetch
: For HTTP requests, prefer usinggoFetch
over wrappingfetch
directly withgo
.goFetch
provides built-in error transformation and a more streamlined API.
See Also
- goSync Function: For synchronous operations.
- goFetch Function: For HTTP requests.
- The Result Type: Detailed explanation of the
Result
type. - Error Handling in go-errors: Comprehensive guide to error handling.
- Working with Custom Error Types: Defining and using custom error types.