@@ -6,6 +6,10 @@ function isAsyncFn(fn: Function) {
6
6
return fn . constructor . name === "AsyncFunction" ;
7
7
}
8
8
9
+ function isResult ( value : unknown ) : value is Result < any , any , any > {
10
+ return value instanceof Ok || value instanceof Err ;
11
+ }
12
+
9
13
interface SyncThenable {
10
14
isSync : true ;
11
15
then < Fn extends ( ) => Promise < any > > ( cb : Fn ) : ReturnType < Fn > ;
@@ -228,7 +232,8 @@ interface IResult<ErrorType, OkType> {
228
232
229
233
/**
230
234
* **Maps a result to another result**
231
- * If the result is success, it will call the callback-function with the encapsulated value, which must return another Result.
235
+ * If the result is success, it will call the callback-function with the encapsulated value, which returns another Result.
236
+ * Nested Results are supported, which will basically act as a flat-map.
232
237
* If the result is failure, it will ignore the callback-function.
233
238
*
234
239
* Example:
@@ -245,15 +250,29 @@ interface IResult<ErrorType, OkType> {
245
250
* // ...
246
251
* }
247
252
*
248
- * const result = doA().map(value => doB(value)); // Result<ErrorA | ErrorB, string>
253
+ * // nested results will flat-map to a single Result...
254
+ * const result1 = doA().map(value => doB(value)); // Result<ErrorA | ErrorB, string>
255
+ *
256
+ * // ...or transform the successful value right away
257
+ * // note: underneath, the callback is wrapped inside Result.safe() in case the callback
258
+ * // might throw
259
+ * const result2 = doA().map(value => value * 2); // Result<ErrorA | Error, number>
249
260
* ```
250
261
*/
251
- map < T extends Result < any , any , any > > (
252
- fn : ( value : OkType ) => T
253
- ) : JoinErrorTypes < ErrorType , T > ;
254
- map < T extends Result < any , any , any > > (
262
+ map < T > (
255
263
fn : ( value : OkType ) => Promise < T >
256
- ) : Promise < JoinErrorTypes < ErrorType , T > > ;
264
+ ) : Promise <
265
+ JoinErrorTypes <
266
+ ErrorType ,
267
+ T extends Result < any , any , any > ? T : Result < Error , T , any >
268
+ >
269
+ > ;
270
+ map < T > (
271
+ fn : ( value : OkType ) => T
272
+ ) : JoinErrorTypes <
273
+ ErrorType ,
274
+ T extends Result < any , any , any > ? T : Result < Error , T , any >
275
+ > ;
257
276
258
277
/**
259
278
* **Rolls back things that were successful**
@@ -380,6 +399,10 @@ export namespace Result {
380
399
return new Err < ErrorType , OkType , RollbackFn > ( error , rollbackFn ) ;
381
400
}
382
401
402
+ type SafeReturnType < E , T > = T extends Result < any , any , any >
403
+ ? Result < E | InferErrorType < T > , InferOkType < T > , never >
404
+ : Result < E , T , never > ;
405
+
383
406
/**
384
407
* **Functions as a try-catch, returning the return-value of the callback on success, or the predefined error or caught error on failure **
385
408
*
@@ -404,39 +427,65 @@ export namespace Result {
404
427
*
405
428
* return value;
406
429
* }); // Result<CustomError, number>
430
+ *
431
+ * // with predefined error Class...
432
+ * const result = Result.safe(CustomError, () => {
433
+ * let value = 2;
434
+ *
435
+ * // code that might throw...
436
+ *
437
+ * return value;
438
+ * }); // Result<CustomError, number>
407
439
* ```
408
440
*/
409
- export function safe < ErrorType , OkType > (
410
- fn : ( ) => Promise < OkType >
411
- ) : Promise < Result < Error , OkType , never > > ;
412
- export function safe < ErrorType , OkType > (
413
- fn : ( ) => OkType
414
- ) : Result < Error , OkType , never > ;
415
- export function safe < ErrorType , OkType > (
416
- err : ErrorType ,
417
- fn : ( ) => Promise < OkType >
418
- ) : Promise < Result < ErrorType , OkType , never > > ;
419
- export function safe < ErrorType , OkType > (
420
- err : ErrorType ,
421
- fn : ( ) => OkType
422
- ) : Result < ErrorType , OkType , never > ;
441
+ export function safe < T > (
442
+ fn : ( ) => Promise < T >
443
+ ) : Promise < SafeReturnType < Error , T > > ;
444
+ export function safe < T > ( fn : ( ) => T ) : SafeReturnType < Error , T > ;
445
+ export function safe < ErrorType , T > (
446
+ err : ErrorType | ( new ( ...args : any [ ] ) => ErrorType ) ,
447
+ fn : ( ) => Promise < T >
448
+ ) : Promise < SafeReturnType < ErrorType , T > > ;
449
+ export function safe < ErrorType , T > (
450
+ err : ErrorType | ( new ( ...args : any [ ] ) => ErrorType ) ,
451
+ fn : ( ) => T
452
+ ) : SafeReturnType < ErrorType , T > ;
423
453
export function safe ( errOrFn : any , fn ?: any ) {
424
454
const hasCustomError = fn !== undefined ;
425
455
426
456
const execute = hasCustomError ? fn : errOrFn ;
427
457
458
+ function getError ( caughtError : Error ) {
459
+ if ( ! hasCustomError ) {
460
+ // just forward the original Error
461
+ return caughtError ;
462
+ }
463
+
464
+ // pass the caught error to the specified constructor
465
+ if ( typeof errOrFn === "function" ) {
466
+ return new errOrFn ( caughtError ) ;
467
+ }
468
+
469
+ // return predefined error
470
+ return errOrFn ;
471
+ }
472
+
428
473
try {
429
474
const resultOrPromise = execute ( ) ;
430
475
431
476
if ( resultOrPromise instanceof Promise ) {
432
477
return resultOrPromise
433
- . then ( okValue => Result . ok ( okValue ) )
434
- . catch ( caughtError => error ( hasCustomError ? errOrFn : caughtError ) ) ;
478
+ . then ( okValue => {
479
+ return isResult ( okValue ) ? okValue : Result . ok ( okValue ) ;
480
+ } )
481
+ . catch ( caughtError => error ( getError ( caughtError ) ) ) ;
435
482
}
436
483
437
- return ok ( resultOrPromise ) ;
484
+ return isResult ( resultOrPromise )
485
+ ? resultOrPromise
486
+ : Result . ok ( resultOrPromise ) ;
438
487
} catch ( caughtError ) {
439
- return error ( hasCustomError ? errOrFn : caughtError ) ;
488
+ return error ( getError ( caughtError ) ) ;
440
489
}
441
490
}
442
491
@@ -650,20 +699,28 @@ abstract class Base<
650
699
throw new Error ( "Method not implemented." ) ;
651
700
}
652
701
653
- map < T extends Result < any , any , any > > (
654
- fn : ( value : OkType ) => T
655
- ) : JoinErrorTypes < ErrorType , T > ;
656
- map < T extends Result < any , any , any > > (
702
+ map < T > (
657
703
fn : ( value : OkType ) => Promise < T >
658
- ) : Promise < JoinErrorTypes < ErrorType , T > > ;
704
+ ) : Promise <
705
+ JoinErrorTypes <
706
+ ErrorType ,
707
+ T extends Result < any , any , any > ? T : Result < Error , T , any >
708
+ >
709
+ > ;
710
+ map < T > (
711
+ fn : ( value : OkType ) => T
712
+ ) : JoinErrorTypes <
713
+ ErrorType ,
714
+ T extends Result < any , any , any > ? T : Result < Error , T , any >
715
+ > ;
659
716
map ( fn : any ) {
660
717
if ( this . isFailure ( ) ) {
661
718
return isAsyncFn ( fn ) ? Promise . resolve ( this ) : this ;
662
719
}
663
720
664
- const result = fn ( ( this as any ) . value ) as any ;
721
+ const result = Result . safe ( ( ) => fn ( ( this as any ) . value ) as any ) ;
665
722
666
- return result ;
723
+ return result as any ;
667
724
}
668
725
669
726
rollback ( ) : RollbackFn extends RollbackFunction
0 commit comments