Skip to content

Commit e6dedd6

Browse files
fix(component-store): allow void callbacks in effect (#3466)
Closes #3462
1 parent 011cbcc commit e6dedd6

File tree

2 files changed

+35
-30
lines changed

2 files changed

+35
-30
lines changed

modules/component-store/spec/types/component-store.types.spec.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -88,24 +88,41 @@ describe('ComponentStore types', () => {
8888
});
8989
});
9090

91-
describe('infers void', () => {
92-
it('when argument type is specified as Observable<void> and nothing is passed', () => {
93-
const effectTest = `const v = componentStore.effect((e: Observable<void>) => string$)();`;
91+
describe('for void types', () => {
92+
it('when generic type is specified as void the argument is optional', () => {
93+
const effectTest = `const sub = componentStore.effect<void>((e) => EMPTY)();`;
9494
expectSnippet(effectTest).toSucceed();
95-
expectSnippet(effectTest).toInfer('v', 'void');
95+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
96+
});
97+
98+
it('when argument type is specified as Observable<void> the argument is optional', () => {
99+
const effectTest = `const sub = componentStore.effect((e: Observable<void>) => EMPTY)();`;
100+
expectSnippet(effectTest).toSucceed();
101+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
96102
});
97103

98-
it('when type is not specified and origin can still be piped', () => {
99-
// treated as Observable<void> 👇
100-
const effectTest = `const v = componentStore.effect((e) => e.pipe(concatMap(() => of())))();`;
104+
it('when type is not specified the argument is optional', () => {
105+
const effectTest = `const sub = componentStore.effect((e) => EMPTY)();`;
101106
expectSnippet(effectTest).toSucceed();
102-
expectSnippet(effectTest).toInfer('v', 'void');
107+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
108+
});
109+
110+
it('when type is specified as void the argument can be a void$', () => {
111+
const effectTest = `const sub = componentStore.effect((e: Observable<void>) => EMPTY)(of<void>());`;
112+
expectSnippet(effectTest).toSucceed();
113+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
114+
});
115+
116+
it('when type is specified as void the argument can be a void', () => {
117+
const effectTest = `const sub = componentStore.effect((e) => EMPTY)({} as unknown as void);`;
118+
expectSnippet(effectTest).toSucceed();
119+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
103120
});
104121

105122
it('when generic type is specified as void and origin can still be piped', () => {
106-
const effectTest = `const v = componentStore.effect<void>((e) => e.pipe(concatMap(() => number$)))();`;
123+
const effectTest = `const sub = componentStore.effect<void>((e) => e.pipe(concatMap(() => number$)))();`;
107124
expectSnippet(effectTest).toSucceed();
108-
expectSnippet(effectTest).toInfer('v', 'void');
125+
expectSnippet(effectTest).toInfer('sub', 'Subscription');
109126
});
110127
});
111128

@@ -146,29 +163,17 @@ describe('ComponentStore types', () => {
146163
);
147164
});
148165

166+
it('when generic type is specified as void and a variable with incorrect type is passed', () => {
167+
expectSnippet(`componentStore.effect<void>((e) => number$)(5);`).toFail(
168+
/Argument of type 'number' is not assignable to parameter of type 'void \| Observable<void>'/
169+
);
170+
});
171+
149172
it('when generic type is specified as unknown and a variable is not passed', () => {
150173
expectSnippet(
151174
`componentStore.effect<unknown>((e) => number$)();`
152175
).toFail(/Expected 1 arguments, but got 0/);
153176
});
154-
155-
it('when argument type is specified as Observable<void> and anything is passed', () => {
156-
expectSnippet(
157-
`componentStore.effect((e: Observable<void>) => string$)(5);`
158-
).toFail(/Expected 0 arguments, but got 1/);
159-
});
160-
161-
it('when type is not specified and anything is passed', () => {
162-
expectSnippet(
163-
`const sub = componentStore.effect((e) => EMPTY)('string');`
164-
).toFail(/Expected 0 arguments, but got 1/);
165-
});
166-
167-
it('when generic type is specified and anything is passed', () => {
168-
expectSnippet(
169-
`componentStore.effect<void>((e) => EMPTY)(undefined);`
170-
).toFail(/Expected 0 arguments, but got 1/);
171-
});
172177
});
173178
});
174179

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,9 +289,9 @@ export class ComponentStore<T extends object> implements OnDestroy {
289289
| unknown = Observable<ProvidedType>,
290290
// Unwrapped actual type of the origin$ Observable, after default was applied
291291
ObservableType = OriginType extends Observable<infer A> ? A : never,
292-
// Return either an empty callback or a function requiring specific types as inputs
292+
// Return either an optional callback or a function requiring specific types as inputs
293293
ReturnType = ProvidedType | ObservableType extends void
294-
? () => void
294+
? (observableOrValue?: void | Observable<void>) => Subscription
295295
: (
296296
observableOrValue: ObservableType | Observable<ObservableType>
297297
) => Subscription

0 commit comments

Comments
 (0)