Basic Usage Patterns
Learn the fundamental usage patterns of go-errors with practical examples.
Basic Usage Patterns
This guide covers the fundamental usage patterns of the go-errors
library, demonstrating how to handle both synchronous and asynchronous operations with Go-style error handling.
Installation
bun add go-errors # or npm install go-errors / yarn add go-errors
Quick Start
import { goSync, goFetch, go } from 'go-errors';
// Synchronous operation with goSync
let [value, err] = goSync(() => {
if (Math.random() > 0.5) {
throw new Error('Something went wrong!');
}
return 42;
});
if (err) {
console.error('Synchronous operation failed:', err.message);
} else {
console.log('Synchronous operation result:', value);
}
// Asynchronous operation with goFetch (for HTTP requests)
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser() {
let [user, fetchErr] = await goFetch<User>('/api/users/123', {
responseTransformer: (data: any) => ({ id: data.id, name: data.name, email: data.email }), // Example transformation
});
if (fetchErr) {
console.error('Failed to fetch user:', fetchErr);
} else {
console.log('Fetched user:', user);
}
}
fetchUser();
// Asynchronous operation with go (for general promises)
async function asyncOperation() {
return new Promise<string>((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("Operation successful!");
} else {
reject(new Error("Operation failed!"));
}
}, 500);
});
}
async function main() {
let [result, asyncErr] = await go(asyncOperation());
if(asyncErr) {
console.error("Async operation failed:", asyncErr.message);
} else {
console.log("Async operation result:", result);
}
}
main();
Core Concepts
Result
Type
The go-errors
uses a tuple-based Result
type to represent the outcome of an operation:
type Result<T, E = Error> = readonly [T, null] | readonly [null, E];
- Success:
[value, null]
- The first element holds the successful result (value
of typeT
), and the second element isnull
. - Failure:
[null, error]
- The first element isnull
, and the second element holds the error (error
of typeE
).
let
Variable Declaration: Always Use Use let
to declare variables that will hold the Result
tuple. This allows you to reuse the err
variable name in subsequent operations, which is a common pattern in Go.
// ✅ Good: Using let
let [value1, err] = goSync(() => someOperation());
if (err) return handleError(err);
let [value2, err] = goSync(() => anotherOperation()); // Reusing 'err'
if (err) return handleError(err);
// ❌ Avoid: Using const (less idiomatic with go-errors)
const [value, error] = goSync(() => someOperation());
Basic Patterns
goSync
)
Synchronous Operations (import { goSync } from 'go-errors';
// Example 1: Simple value return
let [number, err1] = goSync(() => 42);
if (err1) {
console.error("Error:", err1.message); // Won't be reached
} else {
console.log("Number:", number); // Output: Number: 42
}
// Example 2: With error handling
function divide(a: number, b: number): [number | null, Error | null] {
let [result, err] = goSync(() => {
if (b === 0) {
throw new Error('Division by zero');
}
return a / b;
});
if (err) return [null, err]; // Early return on error
return [result, null];
}
let [quotient, divErr] = divide(10, 0);
if (divErr) {
console.error('Division failed:', divErr.message); // Output: Division failed: Division by zero
} else {
console.log('Result:', quotient);
}
go
and goFetch
)
Asynchronous Operations (Using go
(for general Promises):
import { go } from 'go-errors';
async function asyncTask(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.5) {
resolve("Async task successful!");
} else {
reject(new Error("Async task failed!"));
}
}, 500);
});
}
async function runAsyncTask() {
let [result, err] = await go(asyncTask());
if (err) {
console.error("Async task error:", err.message);
} else {
console.log("Async task result:", result);
}
}
runAsyncTask();
Using goFetch
(for HTTP requests):
import { goFetch } from 'go-errors';
interface User {
id: number;
name: string;
email: string;
}
async function fetchUser() {
let [user, err] = await goFetch<User>('/api/users/1', {
responseTransformer: (data: any) => {
// Validate and transform the response data
if (typeof data !== 'object' || data === null || !data.id || !data.name || !data.email) {
throw new Error('Invalid user data received');
}
return { id: data.id, name: data.name, email: data.email };
},
});
if (err) {
console.error('Failed to fetch user:', err);
} else {
console.log('Fetched user:', user);
}
}
fetchUser();
Error Propagation
import { goSync, go } from 'go-errors';
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message);
this.name = 'ValidationError';
}
}
function validateInput(input: string): [string | null, ValidationError | null] {
let [validated, err] = goSync<string, ValidationError>(() => {
if (!input) {
throw new ValidationError('Input cannot be empty', 'input');
}
if (input.length < 3) {
throw new ValidationError('Input must be at least 3 characters long', 'input');
}
return input.trim();
});
if (err) return [null, err] as const; // Propagate the error
return [validated, null] as const;
}
async function processData(input: string) {
let [validatedInput, validationErr] = validateInput(input);
if (validationErr) return [null, validationErr] as const; // Propagate
let [data, fetchErr] = await go(fetch(`/api/data?input=${validatedInput}`));
if (fetchErr) return [null, fetchErr] as const; // Propagate
return [data, null] as const;
}
async function main() {
let [finalResult, finalError] = await processData("");
if(finalError) {
if(finalError instanceof ValidationError) {
console.error("Validation Error:", finalError.message, "Field:", finalError.field);
} else {
console.error("An unexpected error occurred:", finalError);
}
} else {
console.log("Processing successful:", finalResult);
}
}
main();
Best Practices
- Always Use
let
: Uselet
forResult
tuple declarations. - Check Errors First: Always check for errors (
if (err)
) before using the potential value. - Explicit Type Parameters: Specify type parameters (
<T, E>
) when usinggoSync
,go
, andgoFetch
for better type safety and clarity. as const
Assertions: Useas const
when returningResult
tuples to ensure the correct tuple type is inferred.- Custom Error Types: Define and use custom error types for better error handling and context. Use
instanceof
to check for specific error types. - Use
goFetch
for HTTP Requests: PrefergoFetch
over wrappingfetch
directly withgo
.
See Also
- Error Handling in go-errors: Comprehensive guide to error handling.
- Working with Custom Error Types: Create and use your own error types.
- Making HTTP Requests with goFetch: Learn more about using
goFetch
. - API Reference: Detailed API documentation.