Skip to content

Commit 6dbb7a8

Browse files
fix: narrow NamedMutationMethods to just fn signature (#305)
1 parent 74b0e81 commit 6dbb7a8

3 files changed

Lines changed: 63 additions & 9 deletions

File tree

docs/docs/mutations.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,12 +218,16 @@ const save = await store.save({...}); if (inc.status === 'error')
218218
### Signal values
219219

220220
```ts
221-
// Signals
221+
// value: Signal<Result | undefined>;
222+
// status: Signal<MutationStatus>;
223+
// isPending: Signal<boolean>;
224+
// isSuccess: Signal<boolean>;
225+
// error: Signal<unknown>;
222226

223-
// via store
224-
store.increment.value; // also status/error/isPending/status/hasValue;
227+
// via store in `withMutation`
228+
store.incrementValue;
225229

226-
// via member variable
230+
// via member variable if defined outside of `withMutation`
227231
mutationName.value; // ^^^
228232
```
229233

@@ -288,9 +292,8 @@ export type Mutation<Parameter, Result> = {
288292
hasValue(): this is Mutation<Exclude<Parameter, undefined>, Result>; // type narrows `.value()`
289293
};
290294

291-
// Accessed from store or variable
292-
storeName.mutationName.value; // or other signals
293-
mutationName.value; // ^^^
295+
storeName.mutationNameValue; // `withMutations`
296+
mutationName.value; // no `withMutations`
294297
```
295298

296299
#### Callbacks: `onSuccess` and `onError` (optional)

libs/ngrx-toolkit/src/lib/with-mutations.spec.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { fakeAsync, TestBed, tick } from '@angular/core/testing';
22
import { patchState, signalStore, withState } from '@ngrx/signals';
33
import { delay, Observable, of, Subject, switchMap, throwError } from 'rxjs';
44
import { concatOp, exhaustOp, mergeOp, switchOp } from './flattening-operator';
5+
import { Mutation, MutationResult } from './mutation/mutation';
56
import { rxMutation } from './mutation/rx-mutation';
7+
import { Assert, AssertNot, IsEqual } from './test-utils/types';
68
import { withMutations } from './with-mutations';
79

810
type Param =
@@ -534,4 +536,53 @@ describe('withMutations with rxMutation', () => {
534536
error: 'Test-Error',
535537
});
536538
});
539+
540+
it('does not expose mutation properties directly, only the method', async () => {
541+
// Test case for https://github.com/angular-architects/ngrx-toolkit/issues/286
542+
const testSetup = createTestSetup(switchOp);
543+
const store = testSetup.store;
544+
545+
expect(typeof store.increment).toEqual('function');
546+
// @ts-expect-error Should not expose properties, only the method
547+
expect(store.increment.isPending).toBeUndefined();
548+
});
549+
550+
it('types mutation method as plain callable Promise signature and returns nothing else from a mutation', () => {
551+
// Test case for https://github.com/angular-architects/ngrx-toolkit/issues/286
552+
const testSetup = createTestSetup();
553+
const store = testSetup.store;
554+
555+
// Only the method is exposed
556+
type _T1 = Assert<
557+
IsEqual<
558+
typeof store.increment,
559+
(params: Param) => Promise<MutationResult<number>>
560+
>
561+
>;
562+
563+
// Other mutation properties not present
564+
type _T2 = AssertNot<
565+
IsEqual<typeof store.increment, Mutation<Param, number>>
566+
>;
567+
568+
// _T2 in other words:
569+
570+
// CAN...
571+
// Still can call mutation function
572+
store.increment(0);
573+
574+
// CANNOT...
575+
// @ts-expect-error should not expose properties, only method call
576+
const status = store.increment.status;
577+
// @ts-expect-error should not expose properties, only method call
578+
const value = store.increment.value;
579+
// @ts-expect-error should not expose properties, only method call
580+
const isPending = store.increment.isPending;
581+
// @ts-expect-error should not expose properties, only method call
582+
const isSuccess = store.increment.isSuccess;
583+
// @ts-expect-error should not expose properties, only method call
584+
const error = store.increment.error;
585+
// @ts-expect-error should not expose properties, only method call
586+
const hasValue = store.increment.hasValue;
587+
});
537588
});

libs/ngrx-toolkit/src/lib/with-mutations.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
withMethods,
1111
WritableStateSource,
1212
} from '@ngrx/signals';
13-
import { Mutation, MutationStatus } from './mutation/mutation';
13+
import { Mutation, MutationResult, MutationStatus } from './mutation/mutation';
1414

1515
// NamedMutationMethods below will infer the actual parameter and return types
1616
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -33,7 +33,7 @@ type NamedMutationMethods<T extends MutationsDictionary> = {
3333
infer P,
3434
infer R
3535
>
36-
? Mutation<P, R>
36+
? (params: P) => Promise<MutationResult<R>>
3737
: never;
3838
};
3939

0 commit comments

Comments
 (0)