Created
January 6, 2025 05:52
-
-
Save WomB0ComB0/e98ffe77bf85318fe7fac62b79fa9c29 to your computer and use it in GitHub Desktop.
The Result pattern is a functional programming approach that helps you handle success and failure states explicitly, making your code more predictable and easier to maintain.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
type Success<T> = { | |
readonly success: true; | |
readonly value: T; | |
}; | |
type Failure<E> = { | |
readonly success: false; | |
readonly error: E; | |
}; | |
type Result<T, E> = Success<T> | Failure<E>; | |
/** | |
* Creates a successful result | |
* @param value The value to wrap in a success result | |
*/ | |
const success = <T>(value: T): Success<T> => | |
Object.freeze({ success: true, value }); | |
/** | |
* Creates a failed result | |
* @param error The error to wrap in a failure result | |
*/ | |
const failure = <E>(error: E): Failure<E> => | |
Object.freeze({ success: false, error }); | |
type ExtractAsyncArgs<Args extends Array<any>> = Args extends Array<infer PotentialArgTypes> ? [PotentialArgTypes] : [] | |
export const catchError = async <Args extends Array<any>, ReturnType>( | |
asyncFunction: (...args: ExtractAsyncArgs<Args>) => Promise<ReturnType>, | |
...args: ExtractAsyncArgs<Args> | |
): Promise<Result<ReturnType, Error>> => { | |
try { | |
const result = await asyncFunction(...args); | |
return success(result); | |
} catch (error) { | |
return failure(error as Error); | |
} | |
} | |
/** | |
* Maps a successful result to a new value | |
* @param fn Mapping function to apply to the successful value | |
*/ | |
export const map = <T, U, E>( | |
fn: (value: T) => U | |
): (result: Result<T, E>) => Result<U, E> => | |
(result) => | |
result.success ? success(fn(result.value)) : result; | |
/** | |
* Chains a result-returning function after a successful result | |
* @param fn Function that returns a new result | |
*/ | |
export const bind = <T, U, E>( | |
fn: (value: T) => Result<U, E> | |
): (result: Result<T, E>) => Result<U, E> => | |
(result) => | |
result.success ? fn(result.value) : result; | |
/** | |
* Applies a series of functions to an input value, short-circuiting on the first failure | |
* @param input Initial input value | |
* @param functions Array of functions to apply sequentially | |
* @returns Final result after applying all functions or first encountered failure | |
*/ | |
export const railway = <TInput, TOutput, E>( | |
input: TInput, | |
...functions: Array<(input: any) => Result<any, E>> | |
): Result<TOutput, E> => { | |
return functions.reduce<Result<any, E>>( | |
(result, fn) => result.success ? fn(result.value) : result, | |
success(input) | |
); | |
}; | |
/** | |
* Recovers from a failure by applying a function to the error | |
* @param fn Function to handle the error and return a new result | |
*/ | |
export const recover = <T, E1, E2>( | |
fn: (error: E1) => Result<T, E2> | |
): (result: Result<T, E1>) => Result<T, E2> => | |
(result) => | |
result.success ? result : fn(result.error); | |
/** | |
* Taps into a result chain for side effects without modifying the value | |
* @param fn Side effect function to execute on success | |
*/ | |
export const tap = <T, E>( | |
fn: (value: T) => void | |
): (result: Result<T, E>) => Result<T, E> => | |
(result) => { | |
if (result.success) { | |
fn(result.value); | |
} | |
return result; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment