@@ -435,4 +435,124 @@ export interface IMaybe<T> extends IMonad<T> {
435435 * // Converts a Maybe<User> to a Result<User, Error>
436436 */
437437 toResult < E > ( error : E ) : IResult < T , E >
438+
439+ /**
440+ * Chains Maybe operations with a function that returns a Promise.
441+ *
442+ * This allows for seamless integration with asynchronous operations.
443+ * The Promise result is automatically wrapped in a Maybe, with null/undefined
444+ * or rejected promises resulting in None.
445+ *
446+ * Note on resolution preservation: This method preserves both the Maybe context and
447+ * the asynchronous nature of Promises:
448+ * - None values short-circuit (the Promise-returning function is never called)
449+ * - Some values are passed to the function, and its Promise result is processed
450+ * - Promise rejections become None values in the resulting Maybe
451+ * - Promise resolutions become Some values if non-nullish, None otherwise
452+ *
453+ * This approach preserves the monadic semantics while adding asynchronicity.
454+ *
455+ * @typeParam R - The type of the value in the resulting Promise
456+ * @param fn - A function that takes the value from this Maybe and returns a Promise
457+ * @returns A Promise that resolves to a Maybe containing the resolved value
458+ *
459+ * @example
460+ * maybe(userId)
461+ * .flatMapPromise(id => api.fetchUserProfile(id))
462+ * .then(profileMaybe => profileMaybe.match({
463+ * some: profile => displayProfile(profile),
464+ * none: () => showProfileNotFound()
465+ * }));
466+ *
467+ * // Chain multiple promises
468+ * maybe(user)
469+ * .flatMapPromise(user => fetchPermissions(user.id))
470+ * .then(permissionsMaybe => permissionsMaybe.flatMap(permissions =>
471+ * maybe(user).map(user => ({ ...user, permissions }))
472+ * ))
473+ * .then(userWithPermissions => renderUserDashboard(userWithPermissions));
474+ */
475+ flatMapPromise < R > ( fn : ( val : NonNullable < T > ) => Promise < R > ) : Promise < IMaybe < NonNullable < R > > >
476+
477+ /**
478+ * Chains Maybe operations with a function that returns an Observable.
479+ *
480+ * This allows for seamless integration with reactive streams.
481+ * The Observable result is automatically wrapped in a Maybe, with null/undefined
482+ * or empty/error emissions resulting in None.
483+ *
484+ * Note on resolution transformation: This method transforms between context types while
485+ * preserving semantic meaning:
486+ * - None values short-circuit (the Observable-returning function is never called)
487+ * - Some values are passed to the function to generate an Observable
488+ * - Only the first emission from the Observable is captured (timing loss)
489+ * - Observable emissions become Some values in the resulting Maybe
490+ * - Observable completion without emissions or errors becomes None
491+ * - Observable errors become None values
492+ *
493+ * There is timing model transformation: from continuous reactive to one-time asynchronous.
494+ *
495+ * @typeParam R - The type of the value emitted by the resulting Observable
496+ * @param fn - A function that takes the value from this Maybe and returns an Observable
497+ * @returns A Promise that resolves to a Maybe containing the first emitted value
498+ *
499+ * @requires rxjs@^7.0
500+ * @example
501+ * maybe(userId)
502+ * .flatMapObservable(id => userService.getUserSettings(id))
503+ * .then(settingsMaybe => settingsMaybe.match({
504+ * some: settings => applyUserSettings(settings),
505+ * none: () => applyDefaultSettings()
506+ * }));
507+ */
508+ flatMapObservable < R > ( fn : ( val : NonNullable < T > ) => import ( 'rxjs' ) . Observable < R > ) : Promise < IMaybe < NonNullable < R > > >
509+
510+ /**
511+ * Maps and flattens multiple Promises in parallel, preserving the Maybe context.
512+ *
513+ * This operation allows processing an array of async operations concurrently
514+ * while maintaining the Maybe context. If the original Maybe is None, the
515+ * function is never called. Otherwise, all Promises are executed in parallel.
516+ *
517+ * @typeParam R - The type returned by each Promise in the results array
518+ * @param fn - A function that takes the value from this Maybe and returns an array of Promises
519+ * @returns A Promise that resolves to a Maybe containing an array of results
520+ *
521+ * @example
522+ * // Load multiple resources concurrently from a user ID
523+ * maybe(userId)
524+ * .flatMapMany(id => [
525+ * api.fetchProfile(id),
526+ * api.fetchPermissions(id),
527+ * api.fetchSettings(id)
528+ * ])
529+ * .then(resultsMaybe => resultsMaybe.match({
530+ * some: ([profile, permissions, settings]) => displayDashboard(profile, permissions, settings),
531+ * none: () => showError('Failed to load user data')
532+ * }));
533+ */
534+ flatMapMany < R > ( fn : ( val : NonNullable < T > ) => Promise < R > [ ] ) : Promise < IMaybe < NonNullable < R > [ ] > >
535+
536+ /**
537+ * Combines this Maybe with another Maybe using a combiner function.
538+ *
539+ * If both Maybes are Some, applies the function to their values and returns
540+ * a new Some containing the result. If either is None, returns None.
541+ *
542+ * @typeParam U - The type of the value in the other Maybe
543+ * @typeParam R - The type of the combined result
544+ * @param other - Another Maybe to combine with this one
545+ * @param fn - A function that combines the values from both Maybes
546+ * @returns A new Maybe containing the combined result if both inputs are Some, otherwise None
547+ *
548+ * @example
549+ * // Combine user name and email into a display string
550+ * const name = maybe(user.name);
551+ * const email = maybe(user.email);
552+ *
553+ * const display = name.zipWith(email, (name, email) => `${name} <${email}>`);
554+ * // Some("John Doe <[email protected] >") if both name and email exist 555+ * // None if either is missing
556+ */
557+ zipWith < U extends NonNullable < unknown > , R > ( other : IMaybe < U > , fn : ( a : NonNullable < T > , b : U ) => NonNullable < R > ) : IMaybe < R >
438558}
0 commit comments