@@ -759,6 +759,287 @@ export interface IResult<T, E> {
759
759
* });
760
760
*/
761
761
tapFailThru ( fn : ( val : E ) => void ) : IResult < T , E >
762
+
763
+ /**
764
+ * Transforms a Fail Result into an Ok Result using a recovery function.
765
+ *
766
+ * This method provides error handling by:
767
+ * - If this Result is a Fail variant, it applies the function to the error to generate a recovery value
768
+ * and returns a new Ok Result with that value
769
+ * - If this Result is an Ok variant, it returns the original Result unchanged
770
+ *
771
+ * This is similar to a catch block in try/catch, but in a functional style.
772
+ *
773
+ * @param fn - A function that transforms the Fail value into a recovery value
774
+ * @returns An Ok Result with either the original value or the recovered value
775
+ *
776
+ * @example
777
+ * // Providing default values
778
+ * const userResult = getUserById(userId)
779
+ * .recover(error => ({
780
+ * id: 0,
781
+ * name: "Guest User",
782
+ * isGuest: true
783
+ * }));
784
+ *
785
+ * // userResult is guaranteed to be Ok
786
+ * const user = userResult.unwrap(); // Safe, will never throw
787
+ *
788
+ * // Error logging with recovery
789
+ * fetchData()
790
+ * .tapFail(error => logError(error))
791
+ * .recover(error => {
792
+ * const fallbackData = getLocalData();
793
+ * trackRecovery("data_fetch", error);
794
+ * return fallbackData;
795
+ * })
796
+ * .map(data => processData(data)); // This will always run with either fetched or fallback data
797
+ */
798
+ recover ( fn : ( err : E ) => T ) : IResult < T , E >
799
+
800
+ /**
801
+ * Transforms a Fail Result by applying a function that returns another Result.
802
+ *
803
+ * This method provides advanced error recovery by:
804
+ * - If this Result is a Fail variant, it applies the function to the error, which returns a new Result
805
+ * - If this Result is an Ok variant, it returns the original Result unchanged
806
+ *
807
+ * This is useful for fallback operations that might themselves fail.
808
+ *
809
+ * @param fn - A function that takes the Fail value and returns a new Result
810
+ * @returns The original Result if Ok, or the Result returned by the function if Fail
811
+ *
812
+ * @example
813
+ * // Trying a fallback operation that might also fail
814
+ * fetchFromPrimaryAPI()
815
+ * .recoverWith(error => {
816
+ * logFailure(error, "primary_api");
817
+ * // Try the backup API, which might also fail
818
+ * return fetchFromBackupAPI();
819
+ * })
820
+ * .match({
821
+ * ok: data => renderData(data),
822
+ * fail: error => showFatalError("All data sources failed")
823
+ * });
824
+ *
825
+ * // Authentication with multiple strategies
826
+ * authenticateWithPassword(credentials)
827
+ * .recoverWith(error => {
828
+ * if (error.code === "CREDENTIALS_EXPIRED") {
829
+ * return authenticateWithToken(refreshToken);
830
+ * }
831
+ * return fail(error); // Pass through other errors
832
+ * })
833
+ * .recoverWith(error => {
834
+ * if (error.code === "TOKEN_EXPIRED") {
835
+ * return authenticateWithOAuth();
836
+ * }
837
+ * return fail(error); // Pass through other errors
838
+ * });
839
+ */
840
+ recoverWith ( fn : ( err : E ) => IResult < T , E > ) : IResult < T , E >
841
+
842
+ /**
843
+ * Returns this Result if it's Ok, otherwise returns the provided fallback Result.
844
+ *
845
+ * This method allows for specifying an alternative Result:
846
+ * - If this Result is an Ok variant, it is returned unchanged
847
+ * - If this Result is a Fail variant, the fallback Result is returned
848
+ *
849
+ * @param fallback - The Result to return if this Result is Fail
850
+ * @returns This Result if Ok, or the fallback Result if Fail
851
+ *
852
+ * @example
853
+ * // Try multiple data sources in order
854
+ * const userData = getUserFromCache(userId)
855
+ * .orElse(getUserFromDatabase(userId))
856
+ * .orElse(getUserFromBackupService(userId));
857
+ *
858
+ * // Providing a default value as a Result
859
+ * const config = loadConfig()
860
+ * .orElse(ok(DEFAULT_CONFIG));
861
+ *
862
+ * // config is guaranteed to be Ok
863
+ * const configValue = config.unwrap(); // Safe, will never throw
864
+ */
865
+ orElse ( fallback : IResult < T , E > ) : IResult < T , E >
866
+
867
+ /**
868
+ * Swaps the Ok and Fail values, creating a new Result with inversed variants.
869
+ *
870
+ * This method transforms:
871
+ * - An Ok Result into a Fail Result with the same value (now as an error)
872
+ * - A Fail Result into an Ok Result with the same error (now as a value)
873
+ *
874
+ * This can be useful for inverting logic or for protocols where the error case
875
+ * is actually the expected or desired outcome.
876
+ *
877
+ * @returns A new Result with the Ok and Fail variants swapped
878
+ *
879
+ * @example
880
+ * // Inverting validation logic
881
+ * const isInvalid = validateInput(input) // Returns Ok if valid, Fail if invalid
882
+ * .swap() // Returns Fail if valid, Ok if invalid
883
+ * .isOk(); // true if the input was invalid
884
+ *
885
+ * // Working with negative conditions
886
+ * const userNotFound = findUser(userId)
887
+ * .swap()
888
+ * .isOk(); // true if the user was not found
889
+ *
890
+ * // Converting between error domains
891
+ * checkPermission(user, resource) // Returns Ok(true) if permitted, Fail(error) if not
892
+ * .swap() // Returns Fail(true) if permitted, Ok(error) if not
893
+ * .map(error => ({ // Only runs for permission errors
894
+ * type: 'ACCESS_DENIED',
895
+ * message: `Access denied: ${error.message}`,
896
+ * resource
897
+ * }))
898
+ * .swap(); // Back to Ok for permitted, Fail for denied
899
+ */
900
+ swap ( ) : IResult < E , T >
901
+
902
+ /**
903
+ * Combines this Result with another Result using a combining function.
904
+ *
905
+ * This method allows for working with two independent Results together:
906
+ * - If both Results are Ok, applies the function to both values and returns an Ok Result
907
+ * - If either Result is Fail, returns the first Fail Result encountered
908
+ *
909
+ * This is useful for combining data that comes from different sources where both
910
+ * are needed to proceed.
911
+ *
912
+ * @param other - Another Result to combine with this one
913
+ * @param fn - A function that combines the two Ok values
914
+ * @returns A new Result containing either the combined values or the first error
915
+ *
916
+ * @example
917
+ * // Combining user data and preferences that are loaded separately
918
+ * const userData = fetchUserData(userId);
919
+ * const userPrefs = fetchUserPreferences(userId);
920
+ *
921
+ * const userProfile = userData.zipWith(
922
+ * userPrefs,
923
+ * (data, prefs) => ({
924
+ * ...data,
925
+ * preferences: prefs,
926
+ * theme: prefs.theme || 'default'
927
+ * })
928
+ * );
929
+ *
930
+ * // Working with multiple API responses
931
+ * const orders = fetchOrders(userId);
932
+ * const payments = fetchPayments(userId);
933
+ *
934
+ * const combinedData = orders.zipWith(
935
+ * payments,
936
+ * (orderList, paymentList) => {
937
+ * return orderList.map(order => ({
938
+ * ...order,
939
+ * payments: paymentList.filter(p => p.orderId === order.id)
940
+ * }));
941
+ * }
942
+ * );
943
+ */
944
+ zipWith < U , R > ( other : IResult < U , E > , fn : ( a : T , b : U ) => R ) : IResult < R , E >
945
+
946
+ /**
947
+ * Maps the Ok value of this Result to a Promise, and then flattens the resulting structure.
948
+ *
949
+ * This method allows for seamless integration with asynchronous code:
950
+ * - If this Result is an Ok variant, it applies the function to the contained value,
951
+ * awaits the Promise, and wraps the resolved value in a new Ok Result
952
+ * - If the Promise rejects, it returns a Fail Result with the rejection reason
953
+ * - If this Result is a Fail variant, it returns a Promise that resolves to the
954
+ * original Fail Result without calling the function
955
+ *
956
+ * @param fn - A function that takes the Ok value and returns a Promise
957
+ * @returns A Promise that resolves to a new Result
958
+ *
959
+ * @example
960
+ * // Chaining synchronous and asynchronous operations
961
+ * validateUser(userData)
962
+ * .flatMapPromise(user => saveUserToDatabase(user))
963
+ * .then(result => result.match({
964
+ * ok: savedUser => sendWelcomeEmail(savedUser),
965
+ * fail: error => logError("Failed to save user", error)
966
+ * }));
967
+ *
968
+ * // Multi-step asynchronous workflow
969
+ * async function processOrder(orderData) {
970
+ * // Start with sync validation returning a Result
971
+ * const result = await validateOrder(orderData)
972
+ * .flatMapPromise(async order => {
973
+ * // Async payment processing
974
+ * const paymentResult = await processPayment(order.paymentDetails);
975
+ * if (!paymentResult.success) {
976
+ * throw new Error(`Payment failed: ${paymentResult.message}`);
977
+ * }
978
+ *
979
+ * // Async inventory check and allocation
980
+ * await allocateInventory(order.items);
981
+ *
982
+ * // Return the processed order
983
+ * return {
984
+ * ...order,
985
+ * status: 'PAID',
986
+ * paymentId: paymentResult.id
987
+ * };
988
+ * })
989
+ * .flatMapPromise(async paidOrder => {
990
+ * // Final database save
991
+ * const orderId = await saveOrderToDatabase(paidOrder);
992
+ * return { ...paidOrder, id: orderId };
993
+ * });
994
+ *
995
+ * return result;
996
+ * }
997
+ */
998
+ flatMapPromise < M > ( fn : ( val : T ) => Promise < M > ) : Promise < IResult < M , E > >
999
+
1000
+ /**
1001
+ * Maps the Ok value of this Result to an Observable, and then flattens the resulting structure.
1002
+ *
1003
+ * This method allows for seamless integration with reactive code:
1004
+ * - If this Result is an Ok variant, it applies the function to the contained value,
1005
+ * subscribes to the Observable, and wraps the first emitted value in a new Ok Result
1006
+ * - If the Observable errors, it returns a Fail Result with the error
1007
+ * - If the Observable completes without emitting, it returns a Fail Result with the provided default error
1008
+ * - If this Result is a Fail variant, it returns a Promise that resolves to the
1009
+ * original Fail Result without calling the function
1010
+ *
1011
+ * @param fn - A function that takes the Ok value and returns an Observable
1012
+ * @param defaultError - The error to use if the Observable completes without emitting
1013
+ * @returns A Promise that resolves to a new Result
1014
+ *
1015
+ * @requires rxjs@^7.0
1016
+ * @example
1017
+ * // Chaining Result with reactive code
1018
+ * validateUser(userData)
1019
+ * .flatMapObservable(
1020
+ * user => userService.save(user),
1021
+ * new Error("Failed to save user")
1022
+ * )
1023
+ * .then(result => result.match({
1024
+ * ok: savedUser => notifyUserCreated(savedUser),
1025
+ * fail: error => logError("User creation failed", error)
1026
+ * }));
1027
+ *
1028
+ * // Processing real-time data
1029
+ * getSensorData()
1030
+ * .flatMapObservable(
1031
+ * config => sensorApi.connectAndGetReading(config),
1032
+ * new Error("No sensor reading received")
1033
+ * )
1034
+ * .then(result => result.match({
1035
+ * ok: reading => updateDashboard(reading),
1036
+ * fail: error => showSensorError(error)
1037
+ * }));
1038
+ */
1039
+ flatMapObservable < M > (
1040
+ fn : ( val : T ) => import ( 'rxjs' ) . Observable < M > ,
1041
+ defaultError : E
1042
+ ) : Promise < IResult < M , E > >
762
1043
}
763
1044
764
1045
export interface IResultOk < T , E = never > extends IResult < T , E > {
0 commit comments