From 0a13c4d89876f1ed399d2ec3cc46bda933a539b8 Mon Sep 17 00:00:00 2001 From: Tim Deschryver <28659384+timdeschryver@users.noreply.github.com> Date: Mon, 1 May 2023 13:41:58 +0200 Subject: [PATCH] feat(store): add selectSignal options --- modules/store/spec/types/select_signal.spec.ts | 16 ++++++++++++++++ modules/store/src/models.ts | 9 +++++++++ modules/store/src/store.ts | 15 ++++++++++++--- 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/modules/store/spec/types/select_signal.spec.ts b/modules/store/spec/types/select_signal.spec.ts index 1d7409ad5b..f7e0551584 100644 --- a/modules/store/spec/types/select_signal.spec.ts +++ b/modules/store/spec/types/select_signal.spec.ts @@ -41,6 +41,12 @@ describe('Store.selectSignal()', () => { `const selector = store.selectSignal(s => s.foo.bar);` ).toInfer('selector', 'Signal<{ baz: []; }>'); }); + + it('should infer correctly (with options)', () => { + expectSnippet( + `const selector = store.selectSignal(s => s.foo, {equal: (a, b) => a === b});` + ).toInfer('selector', 'Signal<{ bar: { baz: []; }; }>'); + }); }); describe('with selectors', () => { @@ -53,6 +59,16 @@ describe('Store.selectSignal()', () => { `const selector = store.selectSignal(barSelector);` ).toInfer('selector', 'Signal<{ baz: []; }>'); }); + + it('should infer correctly (with options)', () => { + expectSnippet( + `const selector = store.selectSignal(fooSelector, {equal: (a,b) => a === b});` + ).toInfer('selector', 'Signal<{ bar: { baz: []; }; }>'); + + expectSnippet( + `const selector = store.selectSignal(barSelector);` + ).toInfer('selector', 'Signal<{ baz: []; }>'); + }); }); }); }); diff --git a/modules/store/src/models.ts b/modules/store/src/models.ts index a5adf058db..4b923773d6 100644 --- a/modules/store/src/models.ts +++ b/modules/store/src/models.ts @@ -1,3 +1,5 @@ +import { type ValueEqualityFn } from '@angular/core'; + export interface Action { type: string; } @@ -169,3 +171,10 @@ export interface RuntimeChecks { */ strictActionTypeUniqueness?: boolean; } + +export interface SelectSignalOptions { + /** + * A comparison function which defines equality for select results. + */ + equal?: ValueEqualityFn; +} diff --git a/modules/store/src/store.ts b/modules/store/src/store.ts index 72fb47f6c3..77696b1b4c 100644 --- a/modules/store/src/store.ts +++ b/modules/store/src/store.ts @@ -4,7 +4,12 @@ import { Observable, Observer, Operator } from 'rxjs'; import { distinctUntilChanged, map, pluck } from 'rxjs/operators'; import { ActionsSubject } from './actions_subject'; -import { Action, ActionReducer, FunctionIsNotAllowed } from './models'; +import { + Action, + ActionReducer, + SelectSignalOptions, + FunctionIsNotAllowed, +} from './models'; import { ReducerManager } from './reducer_manager'; import { StateObservable } from './state'; import { toSignal } from './to_signal'; @@ -101,9 +106,13 @@ export class Store * Returns a signal of the provided selector. * * @param selector selector function + * @param options select signal options */ - selectSignal(selector: (state: T) => K): Signal { - return computed(() => selector(this.state())); + selectSignal( + selector: (state: T) => K, + options?: SelectSignalOptions + ): Signal { + return computed(() => selector(this.state()), { equal: options?.equal }); } override lift(operator: Operator): Store {