Skip to content

Commit e134eba

Browse files
authored
Merge pull request #19 from seangwright/feat/result-tap-both
Feature: Result.tapEither/Async
2 parents 27c3997 + 1a266a4 commit e134eba

8 files changed

Lines changed: 2933 additions & 4637 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,12 +64,12 @@ The distributed library is currently not minified. Below are the module sizes wh
6464
- maybe.js: 0.81 kb
6565
- maybe.utilities.js: 0.27 kb
6666
- maybeAsync.js: 0.64 kb
67-
- result.js: 1.25 kb
68-
- resultAsync.js: 0.74 kb
67+
- result.js: 1.26 kb
68+
- resultAsync.js: 0.75 kb
6969
- unit.js: 0.13 kb
7070
- utilities.js: 0.27 kb
7171

72-
Total: 4.33 kb
72+
Total: 4.36 kb
7373

7474
### Core Monads
7575

package-lock.json

Lines changed: 2741 additions & 4626 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,18 +48,18 @@
4848
"report:size": "node ./scripts/dist-size.mjs"
4949
},
5050
"devDependencies": {
51-
"@babel/preset-typescript": "7.18.6",
52-
"@types/node": "18.11.18",
53-
"@vitest/coverage-c8": "0.28.1",
51+
"@babel/preset-typescript": "7.21.5",
52+
"@types/node": "18.16.3",
53+
"@vitest/coverage-c8": "0.31.0",
5454
"cpy-cli": "4.2.0",
5555
"gzip-size": "7.0.0",
56-
"jest": "28.1.3",
57-
"prettier": "2.8.3",
56+
"jest": "29.5.0",
57+
"prettier": "2.8.8",
5858
"trash-cli": "5.0.0",
5959
"ts-node": "10.9.1",
60-
"tslib": "2.4.1",
61-
"typescript": "4.9.4",
60+
"tslib": "2.5.0",
61+
"typescript": "5.0.4",
6262
"uglify-js": "3.17.4",
63-
"vitest": "0.28.2"
63+
"vitest": "0.31.0"
6464
}
6565
}

src/result.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Unit } from './unit.js';
33
import {
44
Action,
55
ActionOfT,
6+
AsyncAction,
67
AsyncActionOfT,
78
ErrorHandler,
89
FunctionOfT,
@@ -850,6 +851,26 @@ export class Result<TValue = Unit, TError = string> {
850851
return this;
851852
}
852853

854+
/**
855+
* Executes the action on both success and failure.
856+
* @param action a function with no parameters returning no value
857+
* @returns the current Result
858+
*/
859+
tapEither(action: Action): Result<TValue, TError> {
860+
action();
861+
862+
return this;
863+
}
864+
865+
/**
866+
* Executes the asynchronous action on both success and failure.
867+
* @param action a function
868+
* @returns the current Result wrapped in a ResultAsync
869+
*/
870+
tapEitherAsync(action: AsyncAction): ResultAsync<TValue, TError> {
871+
return ResultAsync.from<TValue, TError>(action().then(() => this));
872+
}
873+
853874
/**
854875
* Maps a successful Result's value to a new value,
855876
* or a failed Result's error to a new value

src/resultAsync.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Result } from './result.js';
22
import { Unit } from './unit.js';
33
import {
4+
Action,
45
ActionOfT,
56
AsyncAction,
67
AsyncActionOfT,
@@ -558,6 +559,32 @@ export class ResultAsync<TValue = Unit, TError = string> {
558559
);
559560
}
560561

562+
/**
563+
* Executes the given async action if the ResultAsync is successful or failed
564+
* @param action an async function
565+
* @returns the current ResultAsync wrapping the inner Result
566+
*/
567+
tapEither(asyncAction: AsyncAction): ResultAsync<TValue, TError>;
568+
/**
569+
* Executes the given action if the ResultAsync is successful or failed
570+
* @param action a function
571+
* @returns the current ResultAsync wrapping the inner Result
572+
*/
573+
tapEither(action: Action): ResultAsync<TValue, TError>;
574+
tapEither(action: Action | AsyncAction): ResultAsync<TValue, TError> {
575+
return new ResultAsync(
576+
this.value.then(async (originalResult) => {
577+
const actionResult = action();
578+
579+
if (actionResult instanceof Promise) {
580+
await actionResult;
581+
}
582+
583+
return originalResult;
584+
})
585+
);
586+
}
587+
561588
/**
562589
*
563590
* @param matcher

test/result/tapEither.spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { Result } from '@/src/result';
2+
3+
describe('Result', () => {
4+
describe('tapEither', () => {
5+
test('will execute the given action if the Result is successful', () => {
6+
let wasCalled = false;
7+
const sut = Result.success(1);
8+
9+
sut.tapEither(() => (wasCalled = true));
10+
11+
expect(wasCalled).toBe(true);
12+
expect(sut).toSucceedWith(1);
13+
});
14+
15+
test('will execute the given action if the Result is a failure', () => {
16+
let wasCalled = false;
17+
const sut = Result.failure<number>('error');
18+
19+
sut.tapEither(() => (wasCalled = true));
20+
21+
expect(wasCalled).toBe(true);
22+
expect(sut).toFailWith('error');
23+
});
24+
});
25+
});

test/result/tapEitherAsync.spec.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Result } from '@/src/result';
2+
3+
describe('Result', () => {
4+
describe('tapEitherAsync', () => {
5+
describe('promise', () => {
6+
test('will execute the asynchronous action if the Result is successful', async () => {
7+
let wasCalled = false;
8+
const sut = Result.success(1);
9+
10+
const asyncAction = () => {
11+
wasCalled = true;
12+
return Promise.resolve();
13+
};
14+
15+
await sut.tapEitherAsync(asyncAction).toPromise();
16+
17+
expect(wasCalled).toBe(true);
18+
expect(sut).toSucceedWith(1);
19+
});
20+
21+
test('will execute the asynchronous action if the Result is a failure', async () => {
22+
let wasCalled = false;
23+
const sut = Result.failure<number>('error');
24+
25+
const asyncAction = () => {
26+
wasCalled = true;
27+
return Promise.resolve();
28+
};
29+
30+
await sut.tapEitherAsync(asyncAction).toPromise();
31+
32+
expect(wasCalled).toBe(true);
33+
expect(sut).toFailWith('error');
34+
});
35+
});
36+
});
37+
});

test/resultAsync/tapEither.spec.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { ResultAsync } from '@/src/resultAsync';
2+
3+
describe('ResultAsync', () => {
4+
describe('tapEither', () => {
5+
test('executes the action with a failed ResultAsync', async () => {
6+
let wasCalled = false;
7+
const sut = ResultAsync.failure<number>('error');
8+
9+
const result = await sut
10+
.tapEither(() => {
11+
wasCalled = true;
12+
})
13+
.toPromise();
14+
15+
expect(result).toFailWith('error');
16+
expect(wasCalled).toBe(true);
17+
});
18+
19+
test('executes the action with a successful ResultAsync', async () => {
20+
const value = 1;
21+
let wasCalled = false;
22+
23+
const sut = ResultAsync.success(value);
24+
25+
const result = await sut
26+
.tapEither(() => {
27+
wasCalled = true;
28+
})
29+
.toPromise();
30+
31+
expect(result).toSucceedWith(1);
32+
expect(wasCalled).toBe(true);
33+
});
34+
35+
test('executes the async action with a successful ResultAsync', async () => {
36+
const value = 1;
37+
let wasCalled = false;
38+
39+
const sut = ResultAsync.success(value);
40+
41+
const result = await sut
42+
.tapEither(() => {
43+
wasCalled = true;
44+
45+
return Promise.resolve();
46+
})
47+
.toPromise();
48+
49+
expect(result).toSucceedWith(1);
50+
expect(wasCalled).toBe(true);
51+
});
52+
53+
test('executes the async action with a failed ResultAsync', async () => {
54+
const error = 'error';
55+
let wasCalled = false;
56+
57+
const sut = ResultAsync.failure(error);
58+
59+
const result = await sut
60+
.tapEither(() => {
61+
wasCalled = true;
62+
63+
return Promise.resolve();
64+
})
65+
.toPromise();
66+
67+
expect(result).toFailWith(error);
68+
expect(wasCalled).toBe(true);
69+
});
70+
});
71+
});

0 commit comments

Comments
 (0)