From 16e0dcedf16f3c0d80f0d98b984b2c29ab798c28 Mon Sep 17 00:00:00 2001 From: ersimont <8042088+ersimont@users.noreply.github.com> Date: Mon, 23 Nov 2020 14:04:11 -0500 Subject: [PATCH] feat(app-state): `spreadArrayStore$()` handles null and undefined Closes #4 --- .../src/lib/utils/spread-array-store.spec.ts | 40 ++++++++++++++----- .../src/lib/utils/spread-array-store.ts | 9 +++-- .../spead-array-store.dts-spec.ts | 15 +++++++ 3 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 projects/app-state/src/typing-tests/spead-array-store.dts-spec.ts diff --git a/projects/app-state/src/lib/utils/spread-array-store.spec.ts b/projects/app-state/src/lib/utils/spread-array-store.spec.ts index 806fe8ef..941ed332 100644 --- a/projects/app-state/src/lib/utils/spread-array-store.spec.ts +++ b/projects/app-state/src/lib/utils/spread-array-store.spec.ts @@ -11,26 +11,26 @@ describe('spreadArrayStore$()', () => { it('emits a separate store object for each element in the array', () => { store.set([1, 2]); - let emitted: Array>; + let emitted!: Array>; spreadArrayStore$(store).subscribe((stores) => { emitted = stores; }); - expect(emitted!.length).toBe(2); - expect(emitted![0].state()).toBe(1); - expect(emitted![1].state()).toBe(2); + expect(emitted.length).toBe(2); + expect(emitted[0].state()).toBe(1); + expect(emitted[1].state()).toBe(2); store.set([3, 4, 5]); - expect(emitted!.length).toBe(3); - expect(emitted![0].state()).toBe(3); - expect(emitted![1].state()).toBe(4); - expect(emitted![2].state()).toBe(5); + expect(emitted.length).toBe(3); + expect(emitted[0].state()).toBe(3); + expect(emitted[1].state()).toBe(4); + expect(emitted[2].state()).toBe(5); store.set([6]); - expect(emitted!.length).toBe(1); - expect(emitted![0].state()).toBe(6); + expect(emitted.length).toBe(1); + expect(emitted[0].state()).toBe(6); store.set([]); - expect(emitted!.length).toBe(0); + expect(emitted.length).toBe(0); }); it('only emits when the length of the array changes', () => { @@ -63,4 +63,22 @@ describe('spreadArrayStore$()', () => { store.set([6]); expect(lastEmit![0]).toBe(previousEmit![0]); }); + + it('treats null and undefined as empty arrays', () => { + interface State { + array?: number[] | null; + } + const arrayStore = new RootStore({})('array'); + let emitted!: Array>; + spreadArrayStore$(arrayStore).subscribe((stores) => { + emitted = stores; + }); + expect(emitted.length).toBe(0); + + arrayStore.set([1]); + expect(emitted.length).toBe(1); + + arrayStore.delete(); + expect(emitted.length).toBe(0); + }); }); diff --git a/projects/app-state/src/lib/utils/spread-array-store.ts b/projects/app-state/src/lib/utils/spread-array-store.ts index 43c50eae..4a00b3ae 100644 --- a/projects/app-state/src/lib/utils/spread-array-store.ts +++ b/projects/app-state/src/lib/utils/spread-array-store.ts @@ -1,5 +1,5 @@ import { Observable } from 'rxjs'; -import { distinctUntilKeyChanged, map } from 'rxjs/operators'; +import { distinctUntilChanged, map } from 'rxjs/operators'; import { Store } from '../index'; /** @@ -25,18 +25,19 @@ import { Store } from '../index'; * ``` */ export function spreadArrayStore$( - source: Store>, + source: Store | null | undefined>, ): Observable>> { let cache: Array> = []; return source.$.pipe( - distinctUntilKeyChanged('length'), + distinctUntilChanged((x, y) => (x?.length || 0) === (y?.length || 0)), map((array) => { + array ??= []; if (array.length < cache.length) { cache = cache.slice(0, array.length); } else { cache = cache.slice(); for (let i = cache.length; i < array.length; ++i) { - cache[i] = source(i); + cache[i] = (source as Store>)(i); } } return cache; diff --git a/projects/app-state/src/typing-tests/spead-array-store.dts-spec.ts b/projects/app-state/src/typing-tests/spead-array-store.dts-spec.ts new file mode 100644 index 00000000..5ef9ad98 --- /dev/null +++ b/projects/app-state/src/typing-tests/spead-array-store.dts-spec.ts @@ -0,0 +1,15 @@ +import { spreadArrayStore$, Store } from '../public-api'; + +declare const array: Store>; +declare const arrayOrNull: Store | null>; +declare const arrayOrUndefined: Store | undefined>; +declare const arrayOrNil: Store | null | undefined>; + +// $ExpectType Observable[]> +spreadArrayStore$(array); +// $ExpectType Observable[]> +spreadArrayStore$(arrayOrNull); +// $ExpectType Observable[]> +spreadArrayStore$(arrayOrUndefined); +// $ExpectType Observable[]> +spreadArrayStore$(arrayOrNil);