Skip to content

Commit 3e02e37

Browse files
fix(component-store): accept error type in tapResponse with strict generic checks (#3068)
Related to #3056
1 parent a39b278 commit 3e02e37

File tree

3 files changed

+123
-2
lines changed

3 files changed

+123
-2
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { EMPTY, noop, Observable, of, throwError } from 'rxjs';
2+
import { tapResponse } from '@ngrx/component-store';
3+
import { concatMap, finalize } from 'rxjs/operators';
4+
5+
describe('tapResponse', () => {
6+
it('should invoke next callback on next', () => {
7+
const nextCallback = jest.fn<void, [number]>();
8+
9+
of(1, 2, 3).pipe(tapResponse(nextCallback, noop)).subscribe();
10+
11+
expect(nextCallback.mock.calls).toEqual([[1], [2], [3]]);
12+
});
13+
14+
it('should invoke error callback on error', () => {
15+
const errorCallback = jest.fn<void, [{ message: string }]>();
16+
const error = { message: 'error' };
17+
18+
throwError(error).pipe(tapResponse(noop, errorCallback)).subscribe();
19+
20+
expect(errorCallback).toHaveBeenCalledWith(error);
21+
});
22+
23+
it('should invoke complete callback on complete', () => {
24+
const completeCallback = jest.fn<void, []>();
25+
26+
EMPTY.pipe(tapResponse(noop, noop, completeCallback)).subscribe();
27+
28+
expect(completeCallback).toHaveBeenCalledWith();
29+
});
30+
31+
it('should not unsubscribe from outer observable on inner observable error', () => {
32+
const innerCompleteCallback = jest.fn<void, []>();
33+
const outerCompleteCallback = jest.fn<void, []>();
34+
35+
new Observable((subscriber) => subscriber.next(1))
36+
.pipe(
37+
concatMap(() =>
38+
throwError('error').pipe(
39+
tapResponse(noop, noop),
40+
finalize(innerCompleteCallback)
41+
)
42+
),
43+
finalize(outerCompleteCallback)
44+
)
45+
.subscribe();
46+
47+
expect(innerCompleteCallback).toHaveBeenCalled();
48+
expect(outerCompleteCallback).not.toHaveBeenCalled();
49+
});
50+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { Expect, expecter } from 'ts-snippet';
2+
import { compilerOptions } from './utils';
3+
4+
describe('tapResponse types', () => {
5+
const snippetFactory = (code: string): string => `
6+
import { tapResponse } from '@ngrx/component-store';
7+
import { noop, of } from 'rxjs';
8+
9+
${code}
10+
`;
11+
12+
function testWith(expectSnippet: (code: string) => Expect): void {
13+
it('should infer next type', () => {
14+
expectSnippet(`
15+
of(1).pipe(
16+
tapResponse((next) => {
17+
const num = next;
18+
}, noop)
19+
);
20+
`).toInfer('num', 'number');
21+
});
22+
23+
it('should accept error type', () => {
24+
expectSnippet(`
25+
of(true).pipe(
26+
tapResponse(noop, (error: { message: string }) => {
27+
const err = error;
28+
})
29+
);
30+
`).toInfer('err', '{ message: string; }');
31+
});
32+
33+
it('should use unknown as default error type', () => {
34+
expectSnippet(`
35+
of(true).pipe(
36+
tapResponse(noop, (error) => {
37+
const err = error;
38+
})
39+
);
40+
`).toInfer('err', 'unknown');
41+
});
42+
}
43+
44+
describe('strict mode', () => {
45+
const expectSnippet = expecter(snippetFactory, {
46+
...compilerOptions(),
47+
strict: true,
48+
});
49+
50+
testWith(expectSnippet);
51+
});
52+
53+
describe('non-strict mode', () => {
54+
const expectSnippet = expecter(snippetFactory, {
55+
...compilerOptions(),
56+
strict: false,
57+
});
58+
59+
testWith(expectSnippet);
60+
});
61+
62+
describe('non-strict mode with strict generic checks', () => {
63+
const expectSnippet = expecter(snippetFactory, {
64+
...compilerOptions(),
65+
strict: false,
66+
noStrictGenericChecks: false,
67+
});
68+
69+
testWith(expectSnippet);
70+
});
71+
});

modules/component-store/src/tap-response.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@ import { catchError, tap } from 'rxjs/operators';
2222
* });
2323
* ```
2424
*/
25-
export function tapResponse<T>(
25+
export function tapResponse<T, E = unknown>(
2626
nextFn: (next: T) => void,
27-
errorFn: <E = unknown>(error: E) => void,
27+
errorFn: (error: E) => void,
2828
completeFn?: () => void
2929
): (source: Observable<T>) => Observable<T> {
3030
return (source) =>

0 commit comments

Comments
 (0)