result-guard
Version:
Type-safe error handling with discriminated unions and type guards for TypeScript
176 lines (174 loc) • 5.77 kB
TypeScript
import { Result, TimeoutOptions, CleanupFunction, EventOptions, IteratorOptions, ConcurrentOptions, CallbackHandlers } from './types';
import { EventEmitter } from 'events';
/**
* Wraps an event emitter or stream operation, handling both promise resolution
* and error events. Automatically cleans up error listeners.
*
* @template T The type of the successful result
* @template E The type of the error (defaults to Error)
* @param emitter The event emitter or stream to handle
* @param operation The operation to perform with the emitter
* @param options Configuration options for event handling
* @returns A Result containing the operation result
*
* @example
* ```typescript
* import { createReadStream } from 'fs';
*
* const readFile = async (path: string) => {
* const stream = createReadStream(path);
* const result = await withEvents(
* stream,
* async () => {
* const chunks = [];
* for await (const chunk of stream) {
* chunks.push(chunk);
* }
* return Buffer.concat(chunks);
* },
* {
* timeout: 5000,
* cleanup: () => stream.destroy()
* }
* );
*
* if (isSuccess(result)) {
* return result.data.toString('utf8');
* }
* throw result.error;
* };
* ```
*/
export declare function withEvents<T, E = Error>(emitter: EventEmitter, operation: () => Promise<T>, options?: EventOptions): Promise<Result<T, E>>;
/**
* Safely handles an async iterator, ensuring proper cleanup even if the iteration
* is interrupted. Also handles timeouts and early termination.
*
* @template T The type of items yielded by the iterator
* @template E The type of the error (defaults to Error)
* @param iterator The async iterator to consume
* @param options Configuration options for iteration
* @returns A Result containing the collected values
*
* @example
* ```typescript
* async function* numberGenerator() {
* for (let i = 0; i < 100; i++) {
* yield i;
* await new Promise(resolve => setTimeout(resolve, 100));
* }
* }
*
* const result = await withIterator(numberGenerator(), {
* timeout: 5000,
* maxItems: 10,
* onItem: value => value < 50 // Stop if value >= 50
* });
*
* if (isSuccess(result)) {
* console.log('Collected values:', result.data);
* }
* ```
*/
export declare function withIterator<T, E = Error>(iterator: AsyncIterator<T>, options?: IteratorOptions<T>): Promise<Result<T[], E>>;
/**
* Wraps a callback-style operation in a Promise with proper cleanup and timeout handling.
* Useful for converting callback-based APIs to promise-based ones.
*
* @template T The type of the successful result
* @template E The type of the error (defaults to Error)
* @param setup Function that sets up callbacks and returns cleanup function
* @param options Configuration options
* @returns A Result containing the operation result
*
* @example
* ```typescript
* const readFileWithCallback = (path: string) => {
* return withCallbacks<Buffer>(({ resolve, reject }) => {
* const stream = createReadStream(path);
* const chunks: Buffer[] = [];
*
* stream.on('data', chunk => chunks.push(chunk));
* stream.on('end', () => resolve(Buffer.concat(chunks)));
* stream.on('error', reject);
*
* return () => stream.destroy();
* }, { timeout: 5000 });
* };
* ```
*/
export declare function withCallbacks<T, E = Error>(setup: (handlers: CallbackHandlers<T>) => CleanupFunction | void, options?: TimeoutOptions): Promise<Result<T, E>>;
/**
* Safely executes multiple concurrent operations, ensuring all operations are
* properly wrapped in tryCatch and handling partial failures. Each operation's
* exact return type is preserved in the result tuple.
*
* @template Ops The tuple type of operations to execute
* @param operations Array of operations to execute concurrently
* @param options Configuration options for concurrency
* @returns A tuple of Results, with each element preserving the exact type of its corresponding operation
*
* @example
* ```typescript
* // Example with typed functions
* interface User { name: string; id: number }
* interface Post { title: string; content: string }
*
* const getUser = async (): Promise<User> => ({ name: 'bob', id: 1 });
* const getPost = async (): Promise<Post> => ({
* title: 'Hello',
* content: 'World'
* });
*
* const results = await concurrent([
* getUser,
* getPost
* ] as const);
*
* const [userResult, postResult] = results;
*
* if (!userResult.isError) {
* const user = userResult.data; // TypeScript knows this is User
* console.log(user.name, user.id);
* }
*
* if (!postResult.isError) {
* const post = postResult.data; // TypeScript knows this is Post
* console.log(post.title, post.content);
* }
*
* // Example with literal types
* const literalResults = await concurrent([
* async () => 42 as const,
* async () => 'hello' as const,
* async () => ({ status: 'ok' as const })
* ] as const);
*
* const [numResult, strResult, objResult] = literalResults;
*
* if (!numResult.isError) {
* const num = numResult.data; // Type is exactly 42
* }
*
* if (!strResult.isError) {
* const str = strResult.data; // Type is exactly 'hello'
* }
*
* if (!objResult.isError) {
* const obj = objResult.data; // Type is { status: 'ok' }
* }
*
* // With concurrency options
* const results = await concurrent(
* [getUser, getPost],
* {
* timeout: 5000,
* maxConcurrent: 2,
* stopOnError: false
* }
* );
* ```
*/
export declare function concurrent<Ops extends readonly (() => Promise<any>)[]>(operations: Ops, options?: ConcurrentOptions): Promise<{
[P in keyof Ops]: Result<Awaited<ReturnType<Ops[P]>>, Error>;
}>;