From 33241cb7992a0043314b37fe4e53147c36e21a29 Mon Sep 17 00:00:00 2001 From: Alex Okrushko Date: Thu, 9 Jan 2020 20:29:14 -0500 Subject: [PATCH] fix(store): allow union of types in props (#2301) --- modules/store/spec/action_creator.spec.ts | 22 ++++++++++++++++++++++ modules/store/src/action_creator.ts | 16 +++++++--------- modules/store/src/models.ts | 21 ++++++++------------- 3 files changed, 37 insertions(+), 22 deletions(-) diff --git a/modules/store/spec/action_creator.spec.ts b/modules/store/spec/action_creator.spec.ts index ddad376ecc..02e0d1c5d6 100644 --- a/modules/store/spec/action_creator.spec.ts +++ b/modules/store/spec/action_creator.spec.ts @@ -76,6 +76,28 @@ describe('Action Creators', () => { narrow(foo({ foo: 42 })); }); + it('should allow the union of types in props', () => { + interface A { + sameProp: 'A'; + } + interface B { + sameProp: 'B'; + extraProp: string; + } + type U = A | B; + const foo = createAction('FOO', props()); + + const fooA = foo({ sameProp: 'A' }); + const fooB = foo({ sameProp: 'B', extraProp: 'allowed' }); + + expect(fooA).toEqual({ type: 'FOO', sameProp: 'A' }); + expect(fooB).toEqual({ + type: 'FOO', + sameProp: 'B', + extraProp: 'allowed', + }); + }); + it('should be serializable', () => { const foo = createAction('FOO', props<{ foo: number }>()); const fooAction = foo({ foo: 42 }); diff --git a/modules/store/src/action_creator.ts b/modules/store/src/action_creator.ts index 585ff6daa6..492fbedf8d 100644 --- a/modules/store/src/action_creator.ts +++ b/modules/store/src/action_creator.ts @@ -3,8 +3,8 @@ import { ActionCreator, TypedAction, FunctionWithParametersType, - PropsReturnType, - DisallowArraysAndTypeProperty, + NotAllowedCheck, + Props, } from './models'; // Action creators taken from ts-action library and modified a bit to better @@ -15,15 +15,15 @@ export function createAction( ): ActionCreator TypedAction>; export function createAction( type: T, - config: { _as: 'props'; _p: P } -): ActionCreator P & TypedAction>; + config: Props

+): ActionCreator) => P & TypedAction>; export function createAction< T extends string, P extends any[], R extends object >( type: T, - creator: Creator> + creator: Creator & NotAllowedCheck ): FunctionWithParametersType> & TypedAction; /** * @description @@ -121,10 +121,8 @@ export function createAction( } } -export function props

(): PropsReturnType

{ - // the return type does not match TypePropertyIsNotAllowed, so double casting - // is used. - return ({ _as: 'props', _p: undefined! } as unknown) as PropsReturnType

; +export function props

(): Props

{ + return { _as: 'props', _p: undefined! }; } export function union< diff --git a/modules/store/src/models.ts b/modules/store/src/models.ts index 542f063b86..e6b56a872f 100644 --- a/modules/store/src/models.ts +++ b/modules/store/src/models.ts @@ -57,10 +57,6 @@ export const arraysAreNotAllowedMsg = 'arrays are not allowed in action creators'; type ArraysAreNotAllowed = typeof arraysAreNotAllowedMsg; -export type DisallowArraysAndTypeProperty = T extends any[] - ? ArraysAreNotAllowed - : T extends { type: any } ? TypePropertyIsNotAllowed : T; - export const typePropertyIsNotAllowedMsg = 'type property is not allowed in action creators'; type TypePropertyIsNotAllowed = typeof typePropertyIsNotAllowedMsg; @@ -75,17 +71,11 @@ export type FunctionIsNotAllowed< export type Creator< P extends any[] = any[], R extends object = object -> = R extends any[] - ? ArraysAreNotAllowed - : R extends { type: any } - ? TypePropertyIsNotAllowed - : FunctionWithParametersType; +> = FunctionWithParametersType; -export type PropsReturnType = T extends any[] +export type NotAllowedCheck = T extends any[] ? ArraysAreNotAllowed - : T extends { type: any } - ? TypePropertyIsNotAllowed - : { _as: 'props'; _p: T }; + : T extends { type: any } ? TypePropertyIsNotAllowed : unknown; /** * See `Creator`. @@ -95,6 +85,11 @@ export type ActionCreator< C extends Creator = Creator > = C & TypedAction; +export interface Props { + _as: 'props'; + _p: T; +} + export type FunctionWithParametersType

= ( ...args: P ) => R;