Essential utility functions for error handling, assertions, and safe function execution. These utilities provide robust patterns for handling failures and ensuring application reliability.
Safely executes a function and returns a tuple containing either the result or the error, following the "never throw" pattern common in functional programming.
/**
* Attempt to execute a function and return the result or error.
* Returns a tuple where:
* - On success: [null, Result] - First element is null, second is the result
* - On error: [Error, null] - First element is the caught error, second is null
*/
function attempt<T, E>(func: () => T): [null, T] | [E, null];Usage Examples:
import { attempt } from 'es-toolkit/util';
// Successful execution
const [error, result] = attempt(() => 42);
// [null, 42]
// Failed execution
const [error, result] = attempt(() => {
throw new Error('Something went wrong');
});
// [Error, null]
// With type parameter
const [error, names] = attempt<string[]>(() => ['Alice', 'Bob']);
// [null, ['Alice', 'Bob']]
// Safe JSON parsing
const [parseError, data] = attempt(() => JSON.parse(jsonString));
if (parseError) {
console.error('Invalid JSON:', parseError.message);
} else {
console.log('Parsed data:', data);
}Safely executes an async function and returns a Promise that resolves to a tuple containing either the result or the error.
/**
* Attempt to execute an async function and return the result or error.
* Returns a Promise that resolves to a tuple where:
* - On success: [null, Result] - First element is null, second is the result
* - On error: [Error, null] - First element is the caught error, second is null
*/
function attemptAsync<T, E>(func: () => Promise<T>): Promise<[null, T] | [E, null]>;Usage Examples:
import { attemptAsync } from 'es-toolkit/util';
// Successful execution
const [error, data] = await attemptAsync(async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
});
// If successful: [null, { ... data ... }]
// Failed execution
const [error, data] = await attemptAsync(async () => {
throw new Error('Network error');
});
// [Error, null]
// With type parameter
const [error, users] = await attemptAsync<User[]>(async () => {
const response = await fetch('https://api.example.com/users');
return response.json();
});
// users is typed as User[]
// Safe API call pattern
async function fetchUserData(userId: string) {
const [error, userData] = await attemptAsync(async () => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return response.json();
});
if (error) {
console.error('Failed to fetch user:', error.message);
return null;
}
return userData;
}Asserts that a given condition is true. If the condition is false, an error is thrown with the provided message or error object. Provides TypeScript assertion support for type narrowing.
/**
* Asserts that a given condition is true. If the condition is false, an error is thrown.
* Supports both string messages and Error objects.
*/
function invariant(condition: unknown, message: string): asserts condition;
function invariant(condition: unknown, error: Error): asserts condition;
function invariant(condition: unknown, message: string | Error): asserts condition;Usage Examples:
import { invariant } from 'es-toolkit/util';
// Basic assertion with string message
invariant(user.isAuthenticated, 'User must be authenticated');
// Custom error object
class ValidationError extends Error {
constructor(field: string) {
super(`Validation failed for field: ${field}`);
this.name = 'ValidationError';
}
}
invariant(email.includes('@'), new ValidationError('email'));
// Type narrowing in TypeScript
function processUser(user: User | null) {
invariant(user !== null, 'User cannot be null');
// TypeScript now knows user is not null
console.log(user.name); // No type error
}
// Ensuring conditions during development
function divide(a: number, b: number): number {
invariant(b !== 0, 'Division by zero is not allowed');
return a / b;
}
// Ensuring object properties exist
function getUsername(config: { user?: { name?: string } }) {
invariant(config.user, 'User configuration is required');
invariant(config.user.name, 'Username is required');
// TypeScript knows config.user.name is defined
return config.user.name;
}Alias for the invariant function, providing familiar Node.js-style assertion API.
/**
* Assert function - alias for invariant
* Asserts that a given condition is true. If the condition is false, an error is thrown.
*/
function assert(condition: unknown, message: string | Error): asserts condition;Usage Examples:
import { assert } from 'es-toolkit/util';
// Node.js-style assertions
assert(process.env.NODE_ENV, 'NODE_ENV must be defined');
assert(typeof port === 'number', 'Port must be a number');
// Development-time checks
function createConnection(config: DatabaseConfig) {
assert(config.host, 'Database host is required');
assert(config.port > 0, 'Database port must be positive');
// Connection logic here
}These utility functions enable robust error handling patterns:
Safe Operations:
// Instead of try/catch blocks everywhere
const [error, result] = attempt(() => riskyOperation());
if (error) {
handleError(error);
} else {
useResult(result);
}Async Safe Operations:
// Clean async error handling
const [error, data] = await attemptAsync(() => fetchData());
if (error) {
return { success: false, error };
}
return { success: true, data };Defensive Programming:
// Ensure preconditions are met
function processPayment(amount: number, currency: string) {
invariant(amount > 0, 'Payment amount must be positive');
invariant(currency.length === 3, 'Currency must be 3-letter code');
// Process payment with confidence
}