From a9bf63c67db85abd23f57bf32432d4d5e2a45610 Mon Sep 17 00:00:00 2001 From: timdeschryver <28659384+timdeschryver@users.noreply.github.com> Date: Wed, 10 Mar 2021 19:14:15 +0100 Subject: [PATCH 1/3] fix(store): allow primitive types --- .../store/spec/types/action_creator.spec.ts | 2 +- .../store/spec/types/reducer_creator.spec.ts | 50 ++++++++++++++++++- modules/store/spec/types/utils.ts | 2 +- modules/store/src/reducer_creator.ts | 4 +- 4 files changed, 54 insertions(+), 4 deletions(-) diff --git a/modules/store/spec/types/action_creator.spec.ts b/modules/store/spec/types/action_creator.spec.ts index b1488374b2..2cc46ab16f 100644 --- a/modules/store/spec/types/action_creator.spec.ts +++ b/modules/store/spec/types/action_creator.spec.ts @@ -92,7 +92,7 @@ describe('createAction()', () => { ); }); - it('should allow foempt', () => { + it('should allow default parameters', () => { expectSnippet(` const foo = createAction('FOO', (bar = 3) => ({bar})); `).toSucceed(); diff --git a/modules/store/spec/types/reducer_creator.spec.ts b/modules/store/spec/types/reducer_creator.spec.ts index ec280189a3..b82716a7d8 100644 --- a/modules/store/spec/types/reducer_creator.spec.ts +++ b/modules/store/spec/types/reducer_creator.spec.ts @@ -4,13 +4,61 @@ import { compilerOptions } from './utils'; describe('createReducer()', () => { const expectSnippet = expecter( (code) => ` - import {createAction, props, on} from '@ngrx/store'; + import {createAction, createReducer, on, props} from '@ngrx/store'; ${code} `, compilerOptions() ); + describe('createReducer()', () => { + it('should support objects', () => { + expectSnippet(` + interface State { name: string }; + const initialState: State = { name: 'sarah' }; + + const setAction = createAction('set', props<{ value: State }>()); + const resetAction = createAction('reset'); + + const reducer = createReducer( + initialState, + on(setAction, (_, { value }) => value), + on(resetAction, () => initialState), + ); + `).toInfer('reducer', 'ActionReducer'); + }); + + it('should support arrays', () => { + expectSnippet(` + const initialState: string[] = []; + + const setAction = createAction('set', props<{ value: string[] }>()); + const resetAction = createAction('reset'); + + const reducer = createReducer( + initialState, + on(setAction, (_, { value }) => value), + on(resetAction, () => initialState), + ); + `).toInfer('reducer', 'ActionReducer'); + }); + + it('should support primitive types', () => { + expectSnippet(` + const initialState: number = 0; + + const setAction = createAction('set', props<{ value: number }>()); + const resetAction = createAction('reset'); + + const reducer = createReducer( + initialState, + on(setAction, (_, { value }) => value), + on(resetAction, () => initialState), + ); + `).toInfer('reducer', 'ActionReducer'); + }); + }); + describe('on()', () => { it('should enforce action property types', () => { expectSnippet(` diff --git a/modules/store/spec/types/utils.ts b/modules/store/spec/types/utils.ts index 63d17eacf0..0c13b2ead8 100644 --- a/modules/store/spec/types/utils.ts +++ b/modules/store/spec/types/utils.ts @@ -1,6 +1,6 @@ export const compilerOptions = () => ({ moduleResolution: 'node', - target: 'es2017', + target: 'es5', baseUrl: '.', experimentalDecorators: true, paths: { diff --git a/modules/store/src/reducer_creator.ts b/modules/store/src/reducer_creator.ts index f52f85b0e5..fe984fed1d 100644 --- a/modules/store/src/reducer_creator.ts +++ b/modules/store/src/reducer_creator.ts @@ -22,7 +22,9 @@ export interface ReducerTypes< // Specialized Reducer that is aware of the Action type it needs to handle export interface OnReducer { - (state: State, action: ActionType): { [P in keyof State]: State[P] }; + (state: State, action: ActionType): State extends object + ? { [P in keyof State]: State[P] } + : State; } /** From 17ac7495b112b169c117479eee10b21c1c88106d Mon Sep 17 00:00:00 2001 From: timdeschryver <28659384+timdeschryver@users.noreply.github.com> Date: Wed, 10 Mar 2021 19:30:46 +0100 Subject: [PATCH 2/3] test(store): add primitive reducer test --- modules/store/spec/reducer_creator.spec.ts | 21 +++++++++++++++++++++ modules/store/spec/types/utils.ts | 2 +- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/modules/store/spec/reducer_creator.spec.ts b/modules/store/spec/reducer_creator.spec.ts index 86f21c2819..3bae1d4be9 100644 --- a/modules/store/spec/reducer_creator.spec.ts +++ b/modules/store/spec/reducer_creator.spec.ts @@ -47,6 +47,27 @@ describe('classes/reducer', function (): void { expect(state).toEqual({ foo: 42, bar: 54 }); }); + it('should create a primitive reducer', () => { + const initialState: number = 0; + const setState = createAction('setState', props<{ value: number }>()); + const resetState = createAction('resetState'); + + const primitiveReducer = createReducer( + initialState, + on(setState, (_state, { value }) => value), + on(resetState, () => initialState) + ); + + let state = primitiveReducer(undefined, { type: 'UNKNOWN' }); + expect(state).toEqual(0); + + state = primitiveReducer(state, setState({ value: 7 })); + expect(state).toEqual(7); + + state = primitiveReducer(state, resetState); + expect(state).toEqual(initialState); + }); + it('should support reducers with multiple actions', () => { type State = string[]; diff --git a/modules/store/spec/types/utils.ts b/modules/store/spec/types/utils.ts index 0c13b2ead8..63d17eacf0 100644 --- a/modules/store/spec/types/utils.ts +++ b/modules/store/spec/types/utils.ts @@ -1,6 +1,6 @@ export const compilerOptions = () => ({ moduleResolution: 'node', - target: 'es5', + target: 'es2017', baseUrl: '.', experimentalDecorators: true, paths: { From 7eb97b15c8f7768beccd8900b33408b6975af03b Mon Sep 17 00:00:00 2001 From: timdeschryver <28659384+timdeschryver@users.noreply.github.com> Date: Wed, 10 Mar 2021 19:34:33 +0100 Subject: [PATCH 3/3] test(store): add action with default parameter test --- modules/store/spec/reducer_creator.spec.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/store/spec/reducer_creator.spec.ts b/modules/store/spec/reducer_creator.spec.ts index 3bae1d4be9..964e2da0ef 100644 --- a/modules/store/spec/reducer_creator.spec.ts +++ b/modules/store/spec/reducer_creator.spec.ts @@ -11,6 +11,13 @@ describe('classes/reducer', function (): void { describe('base', () => { const bar = createAction('[foobar] BAR', props<{ bar: number }>()); const foo = createAction('[foobar] FOO', props<{ foo: number }>()); + const withDefaultParameter = createAction( + '[foobar] withDefaultParameter', + (foo: number = 4, bar: number = 7) => ({ + foo, + bar, + }) + ); describe('on', () => { it('should support reducers with multiple actions', () => { @@ -32,7 +39,11 @@ describe('classes/reducer', function (): void { const fooBarReducer = createReducer( {} as State, on(foo, (state, { foo }) => ({ ...state, foo })), - on(bar, (state, { bar }) => ({ ...state, bar })) + on(bar, (state, { bar }) => ({ ...state, bar })), + on(withDefaultParameter, (_state, { type: _, foo, bar }) => ({ + foo, + bar, + })) ); expect(typeof fooBarReducer).toEqual('function'); @@ -45,6 +56,9 @@ describe('classes/reducer', function (): void { state = fooBarReducer(state, bar({ bar: 54 })); expect(state).toEqual({ foo: 42, bar: 54 }); + + state = fooBarReducer(state, withDefaultParameter()); + expect(state).toEqual({ foo: 4, bar: 7 }); }); it('should create a primitive reducer', () => {