Skip to content

Commit f897cae

Browse files
committed
feat(tap): added 'tap' function
1 parent 7925ebe commit f897cae

File tree

5 files changed

+89
-0
lines changed

5 files changed

+89
-0
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,31 @@ const rejectedTask = Task.resolve(new Error());
104104
const rejectedPromise = toPromise(rejectedTask);
105105
```
106106

107+
### `tap`
108+
109+
The `tap` _function_ can be found in other libreries. It just takes a `callback` _function_ and return a function that calls the `callback` and _returns_ the _argument_. It's useful when you need to perform some side effect. Suppose you need to log the _resolved value_ at certain point of a `Task` _method chaining_. Without the `tap` _function_ you would probably do something like:
110+
111+
```typescript
112+
Task
113+
.resolve(0)
114+
// <Some code here...>
115+
.map(resolvedValue => {
116+
console.log(resolvedValue);
117+
return resolvedValue;
118+
})
119+
// <And more code here...>
120+
```
121+
122+
You can't use an _inline function_ nor a _point-free_ style because you don't want the _log_ to change the `Task`'s _resolved value_, so you need to _return_ it. It's way more fancier and handy to just use the `tap` _function_:
123+
124+
```typescript
125+
Task
126+
.resolve(0)
127+
// <Some code here...>
128+
.map(tap(console.log))
129+
// <And more code here...>
130+
```
131+
107132
### `share`
108133

109134
`task.pipe(share())`

src/tap.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { Task } from '@ts-task/task';
2+
import { jestAssertNever, assertFork } from './testing-utils';
3+
import { tap } from './tap';
4+
5+
describe('tap', () => {
6+
it('`tap` calls the function and returns the argument', cb => {
7+
// GIVEN:
8+
// A callback,
9+
const callback = jest.fn();
10+
// ...a value
11+
const value = 3;
12+
13+
// ...and a Task that is resolved with that value
14+
const task = Task
15+
.resolve(value)
16+
17+
// WHEN:
18+
// The task is mapped with `tap` and the callback
19+
.map(tap(callback))
20+
;
21+
22+
// THEN:
23+
task.fork(
24+
jestAssertNever(cb),
25+
assertFork(cb, resolvedValue => {
26+
// The callback is called with the value
27+
expect(callback).toBeCalledWith(value);
28+
29+
// ...and the value is resolved itself.
30+
expect(resolvedValue).toEqual(value);
31+
})
32+
);
33+
});
34+
});

src/tap.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Calls `fn` performing its side effects but discarding its return value and returning the input parameter instead.
3+
* @param fn Unary function that performs side effects and whose return value will be discarded
4+
* @returns "tapped" `fn`
5+
*/
6+
export const tap = <T> (fn: (x: T) => any) =>
7+
(x: T) => {
8+
fn(x);
9+
return x;
10+
}
11+
;

src/ts-task-utils.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ export * from './case-error';
22
export * from './to-promise';
33
export * from './operators/share';
44
export * from './is-instance-of';
5+
export * from './tap';

test/types/tap.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { tap } from '../../src/tap';
2+
3+
// We will test that the function returned by `tap` mantains its parameter's type
4+
// and also uses it as return type.
5+
6+
// Given a function from number to another type
7+
const stringifyNumber = (x: number) => x.toString(); // $ExpectType (x: number) => string
8+
9+
// ...the tapped version goes from number to number
10+
tap(stringifyNumber); // $ExpectType (x: number) => number
11+
12+
///////////////////////////////////
13+
14+
// Given a function from boolean to another type
15+
const yesOrNo = (x: boolean) => x ? 'Yes' : 'No'; // $ExpectType (x: boolean) => "Yes" | "No"
16+
17+
// ...the tapped versions goes from boolean to boolean
18+
tap(yesOrNo); // $ExpectType (x: boolean) => boolean

0 commit comments

Comments
 (0)