Created
March 21, 2025 09:06
-
-
Save nicolas-zozol/9f3c21f91110649505916f0b1d0b3149 to your computer and use it in GitHub Desktop.
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
| // Define the shape of your safe action result. | |
| export interface ActionResult<T> { | |
| // When execution is successful, data contains the result. | |
| data?: T; | |
| // Optional error information when input validation fails. | |
| validationErrors?: Record<string, any>; | |
| // Optional error information when bound arguments validation fails. | |
| bindArgsValidationErrors?: Record<string, any>; | |
| // Optional error information when the server code throws. | |
| serverError?: any; | |
| } | |
| // The Try monad encapsulates either a success value or an error. | |
| export class Try<T> { | |
| private readonly value?: T; | |
| private readonly error?: unknown; | |
| constructor(value?: T, error?: unknown) { | |
| this.value = value; | |
| this.error = error; | |
| } | |
| /** | |
| * Factory method to build a Try<T> from an ActionResult<T>. | |
| * If the result is undefined or its data is undefined, it's treated as an error. | |
| */ | |
| static fromActionResult<T>(result: ActionResult<T>): Try<T> { | |
| if (!result || result.data === undefined) { | |
| // Create a new error indicating the server returned undefined. | |
| const error = new Error('Server returned undefined or no data.'); | |
| return new Try<T>(undefined, error); | |
| } | |
| return new Try(result.data); | |
| } | |
| /** | |
| * Optionally transform the successful value. | |
| * If there was an error, the error is passed along. | |
| * | |
| * @param fn Transformation function applied on the successful value. | |
| */ | |
| with<U>(fn: (value: T) => U): Try<U> { | |
| if (this.error !== undefined || this.value === undefined) { | |
| // Propagate the error if it exists. | |
| return new Try<U>(undefined, this.error); | |
| } | |
| try { | |
| // Apply transformation if no error. | |
| return new Try<U>(fn(this.value)); | |
| } catch (err) { | |
| // In case the transformation itself throws, capture the error. | |
| return new Try<U>(undefined, err); | |
| } | |
| } | |
| /** | |
| * Terminal operation that returns the value if successful, | |
| * or the provided default value if there was an error. | |
| * | |
| * @param defaultValue A fallback value to use when an error is present. | |
| */ | |
| fallback(defaultValue: T): T { | |
| return this.error !== undefined || this.value === undefined | |
| ? defaultValue | |
| : this.value; | |
| } | |
| /** | |
| * Terminal operation that handles the error. | |
| * The provided handler is called only if there was an error. | |
| * | |
| * @param fn Error handler that returns a value. | |
| */ | |
| catch(fn: (error: unknown) => T): T { | |
| return this.error !== undefined || this.value === undefined | |
| ? fn(this.error) | |
| : this.value; | |
| } | |
| } | |
| /** | |
| * Helper function to wrap a safe action result in the Try monad. | |
| * It now accepts `undefined` and treats it as a server error. | |
| * | |
| * @param result The ActionResult<T> from a safe action, or undefined. | |
| */ | |
| export function tryOf<T>(result: ActionResult<T> | undefined): Try<T> { | |
| if (!result) { | |
| return new Try<T>(undefined, new Error('Server returned undefined.')); | |
| } | |
| return Try.fromActionResult(result); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment