@@ -35,6 +35,9 @@ For asynchronous tasks, `neverthrow` offers a `ResultAsync` class which wraps a
35
35
- [ ` Result.orElse ` (method)] ( #resultorelse-method )
36
36
- [ ` Result.match ` (method)] ( #resultmatch-method )
37
37
- [ ` Result.asyncMap ` (method)] ( #resultasyncmap-method )
38
+ - [ ` Result.andTee ` (method)] ( #resultandtee-method )
39
+ - [ ` Result.andThrough ` (method)] ( #resultandthrough-method )
40
+ - [ ` Result.asyncAndThrough ` (method)] ( #resultasyncandthrough-method )
38
41
- [ ` Result.fromThrowable ` (static class method)] ( #resultfromthrowable-static-class-method )
39
42
- [ ` Result.combine ` (static class method)] ( #resultcombine-static-class-method )
40
43
- [ ` Result.combineWithAllErrors ` (static class method)] ( #resultcombinewithallerrors-static-class-method )
@@ -51,6 +54,8 @@ For asynchronous tasks, `neverthrow` offers a `ResultAsync` class which wraps a
51
54
- [ ` ResultAsync.andThen ` (method)] ( #resultasyncandthen-method )
52
55
- [ ` ResultAsync.orElse ` (method)] ( #resultasyncorelse-method )
53
56
- [ ` ResultAsync.match ` (method)] ( #resultasyncmatch-method )
57
+ - [ ` ResultAsync.andTee ` (method)] ( #resultasyncandtee-method )
58
+ - [ ` ResultAsync.andThrough ` (method)] ( #resultasyncandthrough-method )
54
59
- [ ` ResultAsync.combine ` (static class method)] ( #resultasynccombine-static-class-method )
55
60
- [ ` ResultAsync.combineWithAllErrors ` (static class method)] ( #resultasynccombinewithallerrors-static-class-method )
56
61
- [ ` ResultAsync.safeUnwrap() ` ] ( #resultasyncsafeunwrap )
@@ -541,6 +546,136 @@ Note that in the above example if `parseHeaders` returns an `Err` then `.map` an
541
546
542
547
---
543
548
549
+ #### ` Result.andTee ` (method)
550
+
551
+ Takes a ` Result<T, E> ` and lets the original ` Result<T, E> ` pass through regardless the result of the passed-in function.
552
+ This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
553
+
554
+ ** Signature:**
555
+
556
+ ``` typescript
557
+ class Result <T , E > {
558
+ andTee(
559
+ callback : (value : T ) => unknown
560
+ ): Result <T , E > { ... }
561
+ }
562
+ ```
563
+
564
+ ** Example:**
565
+
566
+ ``` typescript
567
+ import { parseUserInput } from ' imaginary-parser'
568
+ import { logUser } from ' imaginary-logger'
569
+ import { insertUser } from ' imaginary-database'
570
+
571
+ // ^ assume parseUserInput, logUser and insertUser have the following signatures:
572
+ // parseUserInput(input: RequestData): Result<User, ParseError>
573
+ // logUser(user: User): Result<void, LogError>
574
+ // insertUser(user: User): ResultAsync<void, InsertError>
575
+ // Note logUser returns void upon success but insertUser takes User type.
576
+
577
+ const resAsync = parseUserInput (userInput )
578
+ .andTee (logUser )
579
+ .asyncAndThen (insertUser )
580
+
581
+ // Note no LogError shows up in the Result type
582
+ resAsync .then ((res : Result <void , ParseError | InsertError >) => {e
583
+ if (res .isErr ()){
584
+ console .log (" Oops, at least one step failed" , res .error )
585
+ }
586
+ else {
587
+ console .log (" User input has been parsed and inserted successfully." )
588
+ }
589
+ }))
590
+ ```
591
+
592
+ [ ⬆️ Back to top] ( #toc )
593
+
594
+ ---
595
+
596
+ #### ` Result.andThrough ` (method)
597
+
598
+ Similar to ` andTee ` except for:
599
+
600
+ - when there is an error from the passed-in function, that error will be passed along.
601
+
602
+ ** Signature:**
603
+
604
+ ``` typescript
605
+ class Result <T , E > {
606
+ andThrough<F >(
607
+ callback : (value : T ) => Result <unknown , F >
608
+ ): Result <T , E | F > { ... }
609
+ }
610
+ ```
611
+
612
+ ** Example:**
613
+
614
+ ``` typescript
615
+ import { parseUserInput } from ' imaginary-parser'
616
+ import { validateUser } from ' imaginary-validator'
617
+ import { insertUser } from ' imaginary-database'
618
+
619
+ // ^ assume parseUseInput, validateUser and insertUser have the following signatures:
620
+ // parseUserInput(input: RequestData): Result<User, ParseError>
621
+ // validateUser(user: User): Result<void, ValidateError>
622
+ // insertUser(user: User): ResultAsync<void, InsertError>
623
+ // Note validateUser returns void upon success but insertUser takes User type.
624
+
625
+ const resAsync = parseUserInput (userInput )
626
+ .andThrough (validateUser )
627
+ .asyncAndThen (insertUser )
628
+
629
+ resAsync .then ((res : Result <void , ParseErro | ValidateError | InsertError >) => {e
630
+ if (res .isErr ()){
631
+ console .log (" Oops, at least one step failed" , res .error )
632
+ }
633
+ else {
634
+ console .log (" User input has been parsed, validated, inserted successfully." )
635
+ }
636
+ }))
637
+ ```
638
+
639
+ [ ⬆️ Back to top] ( #toc )
640
+
641
+ ---
642
+
643
+ #### ` Result.asyncAndThrough ` (method)
644
+
645
+ Similar to ` andThrough ` except you must return a ResultAsync.
646
+
647
+ You can then chain the result of ` asyncAndThrough ` using the ` ResultAsync ` apis (like ` map ` , ` mapErr ` , ` andThen ` , etc.)
648
+
649
+ ** Signature:**
650
+
651
+ ``` typescript
652
+ import { parseUserInput } from ' imaginary-parser'
653
+ import { insertUser } from ' imaginary-database'
654
+ import { sendNotification } from ' imaginary-service'
655
+
656
+ // ^ assume parseUserInput, insertUser and sendNotification have the following signatures:
657
+ // parseUserInput(input: RequestData): Result<User, ParseError>
658
+ // insertUser(user: User): ResultAsync<void, InsertError>
659
+ // sendNotification(user: User): ResultAsync<void, NotificationError>
660
+ // Note insertUser returns void upon success but sendNotification takes User type.
661
+
662
+ const resAsync = parseUserInput (userInput )
663
+ .asyncAndThrough (insertUser )
664
+ .andThen (sendNotification )
665
+
666
+ resAsync .then ((res : Result <void , ParseError | InsertError | NotificationError >) => {e
667
+ if (res .isErr ()){
668
+ console .log (" Oops, at least one step failed" , res .error )
669
+ }
670
+ else {
671
+ console .log (" User has been parsed, inserted and notified successfully." )
672
+ }
673
+ }))
674
+ ```
675
+
676
+ [ ⬆️ Back to top] ( #toc )
677
+
678
+ ---
544
679
#### ` Result.fromThrowable ` (static class method)
545
680
546
681
> Although Result is not an actual JS class, the way that ` fromThrowable ` has been implemented requires that you call ` fromThrowable ` as though it were a static method on ` Result ` . See examples below.
@@ -1096,7 +1231,101 @@ const resultMessage = await validateUser(user)
1096
1231
[ ⬆️ Back to top] ( #toc )
1097
1232
1098
1233
---
1234
+ #### ` ResultAsync.andTee ` (method)
1235
+
1236
+ Takes a ` ResultAsync<T, E> ` and lets the original ` ResultAsync<T, E> ` pass through regardless
1237
+ the result of the passed-in function.
1238
+ This is a handy way to handle side effects whose failure or success should not affect your main logics such as logging.
1239
+
1240
+ ** Signature:**
1241
+
1242
+ ``` typescript
1243
+ class ResultAsync <T , E > {
1244
+ andTee(
1245
+ callback : (value : T ) => unknown
1246
+ ): ResultAsync <T , E > => { ... }
1247
+ }
1248
+ ```
1249
+
1250
+ ** Example:**
1251
+
1252
+ ``` typescript
1253
+ import { insertUser } from ' imaginary-database'
1254
+ import { logUser } from ' imaginary-logger'
1255
+ import { sendNotification } from ' imaginary-service'
1256
+
1257
+ // ^ assume insertUser, logUser and sendNotification have the following signatures:
1258
+ // insertUser(user: User): ResultAsync<User, InsertError>
1259
+ // logUser(user: User): Result<void, LogError>
1260
+ // sendNotification(user: User): ResultAsync<void, NotificationError>
1261
+ // Note logUser returns void on success but sendNotification takes User type.
1262
+
1263
+ const resAsync = insertUser (user )
1264
+ .andTee (logUser )
1265
+ .andThen (sendNotification )
1266
+
1267
+ // Note there is no LogError in the types below
1268
+ resAsync .then ((res : Result <void , InsertError | NotificationError >) => {e
1269
+ if (res .isErr ()){
1270
+ console .log (" Oops, at least one step failed" , res .error )
1271
+ }
1272
+ else {
1273
+ console .log (" User has been inserted and notified successfully." )
1274
+ }
1275
+ }))
1276
+ ```
1099
1277
1278
+ [ ⬆️ Back to top] ( #toc )
1279
+
1280
+ ---
1281
+ #### ` ResultAsync.andThrough ` (method)
1282
+
1283
+
1284
+ Similar to ` andTee ` except for:
1285
+
1286
+ - when there is an error from the passed-in function, that error will be passed along.
1287
+
1288
+ ** Signature:**
1289
+
1290
+ ``` typescript
1291
+ class ResultAsync <T , E > {
1292
+ andThrough<F >(
1293
+ callback : (value : T ) => Result <unknown , F > | ResultAsync <unknown , F >,
1294
+ ): ResultAsync <T , E | F > => { ... }
1295
+ }
1296
+ ```
1297
+
1298
+ ** Example:**
1299
+
1300
+ ``` typescript
1301
+
1302
+ import { buildUser } from ' imaginary-builder'
1303
+ import { insertUser } from ' imaginary-database'
1304
+ import { sendNotification } from ' imaginary-service'
1305
+
1306
+ // ^ assume buildUser, insertUser and sendNotification have the following signatures:
1307
+ // buildUser(userRaw: UserRaw): ResultAsync<User, BuildError>
1308
+ // insertUser(user: User): ResultAsync<void, InsertError>
1309
+ // sendNotification(user: User): ResultAsync<void, NotificationError>
1310
+ // Note insertUser returns void upon success but sendNotification takes User type.
1311
+
1312
+ const resAsync = buildUser (userRaw )
1313
+ .andThrough (insertUser )
1314
+ .andThen (sendNotification )
1315
+
1316
+ resAsync .then ((res : Result <void , BuildError | InsertError | NotificationError >) => {e
1317
+ if (res .isErr ()){
1318
+ console .log (" Oops, at least one step failed" , res .error )
1319
+ }
1320
+ else {
1321
+ console .log (" User data has been built, inserted and notified successfully." )
1322
+ }
1323
+ }))
1324
+ ```
1325
+
1326
+ [ ⬆️ Back to top] ( #toc )
1327
+
1328
+ ---
1100
1329
#### ` ResultAsync.combine ` (static class method)
1101
1330
1102
1331
Combine lists of ` ResultAsync ` s.
0 commit comments