From 54ef3a13c497b39089da429bb4c2cc672d6f9ed4 Mon Sep 17 00:00:00 2001 From: alatushkin Date: Fri, 1 Sep 2017 09:26:53 +0300 Subject: [PATCH 01/10] Improve withHandler & withStateHandlers Improve withHandler & withStateHandlers to restrict updaterName/handlerName to corresponding type filed names --- types/recompose/index.d.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index 5fe0c82987..fe93a01466 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -78,12 +78,12 @@ declare module 'recompose' { // withHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withhandlers type EventHandler = Function; - type HandleCreators = { - [handlerName: string]: mapper; + type HandleCreators = { + [handlerName in keyof THandlers]: mapper; }; - type HandleCreatorsFactory = (initialProps: TOutter) => HandleCreators; + type HandleCreatorsFactory = (initialProps: TOutter) => HandleCreators; export function withHandlers( - handlerCreators: HandleCreators | HandleCreatorsFactory + handlerCreators: HandleCreators | HandleCreatorsFactory ): InferableComponentEnhancerWithProps; // defaultProps: https://github.com/acdlite/recompose/blob/master/docs/API.md#defaultprops @@ -134,12 +134,12 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateUpdaters = { - [updaterName: string]: (state: TState, props: TOutter) => StateHandler; + type StateUpdaters = { + [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; }; export function withStateHandlers( createProps: TState | mapper, - stateUpdaters: StateUpdaters, + stateUpdaters: StateUpdaters, ): InferableComponentEnhancerWithProps; // withReducer: https://github.com/acdlite/recompose/blob/master/docs/API.md#withReducer From eb793e421105e24af1362fda037aa3bd9882615c Mon Sep 17 00:00:00 2001 From: alatushkin Date: Fri, 1 Sep 2017 09:41:07 +0300 Subject: [PATCH 02/10] Update index.d.ts --- types/recompose/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index fe93a01466..eeb693f14e 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -134,7 +134,7 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateUpdaters = { + type StateUpdaters = { [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; }; export function withStateHandlers( From 8c741b68a967ee0062b75a7465c742b9e031dc6c Mon Sep 17 00:00:00 2001 From: alatushkin Date: Fri, 1 Sep 2017 09:41:39 +0300 Subject: [PATCH 03/10] Update index.d.ts --- types/recompose/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index eeb693f14e..5a9ab9d97e 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -134,7 +134,7 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateUpdaters = { + type StateUpdaters = { [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; }; export function withStateHandlers( From 90307a1344a16810328a001c9572c0e496c69b00 Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 12:38:19 +0000 Subject: [PATCH 04/10] use string literals instead of arbitrary interface to define withStateHandlers names --- types/recompose/index.d.ts | 39 +++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index 5a9ab9d97e..98b4ff49df 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -134,13 +134,42 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateUpdaters = { - [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; + type StateUpdaters< + TOutter, + TState, + TUpdater1 extends string, + TUpdater2 extends string = TUpdater1, + TUpdater3 extends string = TUpdater2, + TUpdater4 extends string = TUpdater3, + TUpdater5 extends string = TUpdater4 + > = { [updaterName in + | TUpdater1 + | TUpdater2 + | TUpdater3 + | TUpdater4 + | TUpdater5 + ]: (state: TState, props: TOutter) => StateHandler; }; - export function withStateHandlers( + export function withStateHandlers< + TState, + TOutter extends {} = {}, + TUpdater1 extends string = "update", + TUpdater2 extends string = TUpdater1, + TUpdater3 extends string = TUpdater2, + TUpdater4 extends string = TUpdater3, + TUpdater5 extends string = TUpdater4 + >( createProps: TState | mapper, - stateUpdaters: StateUpdaters, - ): InferableComponentEnhancerWithProps; + stateUpdaters: StateUpdaters, + ): InferableComponentEnhancerWithProps< + { [updaterName in + | TUpdater1 + | TUpdater2 + | TUpdater3 + | TUpdater4 + | TUpdater5 + ]: StateHandler + } & TState & TOutter, TOutter>; // withReducer: https://github.com/acdlite/recompose/blob/master/docs/API.md#withReducer type reducer = (s: TState, a: TAction) => TState; From 80b3b0b5f20a8c4d24ad42c0d343f19096197691 Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 13:41:36 +0000 Subject: [PATCH 05/10] replace string literals in withStateHandlers with reusable StateHandlerMap --- types/recompose/index.d.ts | 43 ++++++++--------------------- types/recompose/recompose-tests.tsx | 9 ++++-- 2 files changed, 19 insertions(+), 33 deletions(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index 98b4ff49df..9e96ee366a 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -134,42 +134,23 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateUpdaters< - TOutter, - TState, - TUpdater1 extends string, - TUpdater2 extends string = TUpdater1, - TUpdater3 extends string = TUpdater2, - TUpdater4 extends string = TUpdater3, - TUpdater5 extends string = TUpdater4 - > = { [updaterName in - | TUpdater1 - | TUpdater2 - | TUpdater3 - | TUpdater4 - | TUpdater5 - ]: (state: TState, props: TOutter) => StateHandler; + + type StateHandlerMap = { + [updaterName: string]: StateHandler; }; + + type StateUpdaters = { + [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; + }; + export function withStateHandlers< TState, - TOutter extends {} = {}, - TUpdater1 extends string = "update", - TUpdater2 extends string = TUpdater1, - TUpdater3 extends string = TUpdater2, - TUpdater4 extends string = TUpdater3, - TUpdater5 extends string = TUpdater4 + TUpdaters extends StateHandlerMap, + TOutter extends {} = {} >( createProps: TState | mapper, - stateUpdaters: StateUpdaters, - ): InferableComponentEnhancerWithProps< - { [updaterName in - | TUpdater1 - | TUpdater2 - | TUpdater3 - | TUpdater4 - | TUpdater5 - ]: StateHandler - } & TState & TOutter, TOutter>; + stateUpdaters: StateUpdaters, + ): InferableComponentEnhancerWithProps; // withReducer: https://github.com/acdlite/recompose/blob/master/docs/API.md#withReducer type reducer = (s: TState, a: TAction) => TState; diff --git a/types/recompose/recompose-tests.tsx b/types/recompose/recompose-tests.tsx index 3bffa12cc6..9f62e8782c 100644 --- a/types/recompose/recompose-tests.tsx +++ b/types/recompose/recompose-tests.tsx @@ -18,6 +18,8 @@ import { createEventHandlerWithConfig, componentFromStreamWithConfig, mapPropsStreamWithConfig, setObservableConfig, + StateHandler, + StateHandlerMap, } from "recompose"; import rxjsconfig from "recompose/rxjsObservableConfig"; import rxjs4config from "recompose/rxjs4ObservableConfig"; @@ -186,11 +188,14 @@ function testWithState() { function testWithStateHandlers() { interface State { counter: number; } - interface Updaters { add: (n: number) => State; } - type InnerProps = State & Updaters; + interface Updaters extends StateHandlerMap { + add: StateHandler; + } interface OutterProps { initialCounter: number, power: number } + type InnerProps = State & Updaters & OutterProps; const InnerComponent: React.StatelessComponent = (props) =>
+
{`Initial counter: ${props.initialCounter}`}
{`Counter: ${props.counter}`}
props.add(2)}>
; From 34db3d70703c6f8c84d924e869902a6059106072 Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 13:47:46 +0000 Subject: [PATCH 06/10] add ExpectError test to prove typechecking of updater keys --- types/recompose/recompose-tests.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/recompose/recompose-tests.tsx b/types/recompose/recompose-tests.tsx index 9f62e8782c..8c175af821 100644 --- a/types/recompose/recompose-tests.tsx +++ b/types/recompose/recompose-tests.tsx @@ -210,7 +210,12 @@ function testWithStateHandlers() { const rendered = ( ); -} + + const broken = withStateHandlers( + (props: OutterProps) => ({ counter: props.initialCounter }), + { notAKeyOfUpdaters: (state, props) => n => ({ ...state, counter: state.counter + n ** props.power }), }, // $ExpectError + ); + } function testWithReducer() { interface State { count: number } From ee7e7a4085df7b51abbd13befeea857168079f64 Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 13:54:26 +0000 Subject: [PATCH 07/10] tidy up --- types/recompose/index.d.ts | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index 9e96ee366a..ad855cae69 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -134,20 +134,13 @@ declare module 'recompose' { // withStateHandlers: https://github.com/acdlite/recompose/blob/master/docs/API.md#withstatehandlers type StateHandler = (...payload: any[]) => TState | undefined; - type StateHandlerMap = { [updaterName: string]: StateHandler; }; - type StateUpdaters = { [updaterName in keyof TUpdaters]: (state: TState, props: TOutter) => StateHandler; }; - - export function withStateHandlers< - TState, - TUpdaters extends StateHandlerMap, - TOutter extends {} = {} - >( + export function withStateHandlers, TOutter = {}>( createProps: TState | mapper, stateUpdaters: StateUpdaters, ): InferableComponentEnhancerWithProps; From 49601286523834bc34e11791ec4cd4be9db974bf Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 13:59:41 +0000 Subject: [PATCH 08/10] add test for withHandlers too --- types/recompose/recompose-tests.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/types/recompose/recompose-tests.tsx b/types/recompose/recompose-tests.tsx index 8c175af821..99517de01a 100644 --- a/types/recompose/recompose-tests.tsx +++ b/types/recompose/recompose-tests.tsx @@ -119,6 +119,11 @@ function testWithHandlers() { out={42} /> ) + + const handlerNameTypecheckProof = withHandlers((props) => ({ // $ExpectError + onChange: () => () => {}, // $ExpectError + notAKeyOnHandlerProps: () => () => {}, // $ExpectError + })); // $ExpectError } function testDefaultProps() { @@ -211,7 +216,7 @@ function testWithStateHandlers() { ); - const broken = withStateHandlers( + const updateNameTypecheckProof = withStateHandlers( (props: OutterProps) => ({ counter: props.initialCounter }), { notAKeyOfUpdaters: (state, props) => n => ({ ...state, counter: state.counter + n ** props.power }), }, // $ExpectError ); From abb76936d70f235af84dad95b6a1186f75a89ef1 Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 14:07:49 +0000 Subject: [PATCH 09/10] define withHandlers as providing its props to its child --- types/recompose/index.d.ts | 2 +- types/recompose/recompose-tests.tsx | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/types/recompose/index.d.ts b/types/recompose/index.d.ts index ad855cae69..a9d807360b 100644 --- a/types/recompose/index.d.ts +++ b/types/recompose/index.d.ts @@ -84,7 +84,7 @@ declare module 'recompose' { type HandleCreatorsFactory = (initialProps: TOutter) => HandleCreators; export function withHandlers( handlerCreators: HandleCreators | HandleCreatorsFactory - ): InferableComponentEnhancerWithProps; + ): InferableComponentEnhancerWithProps; // defaultProps: https://github.com/acdlite/recompose/blob/master/docs/API.md#defaultprops export function defaultProps( diff --git a/types/recompose/recompose-tests.tsx b/types/recompose/recompose-tests.tsx index 99517de01a..a780fc7ded 100644 --- a/types/recompose/recompose-tests.tsx +++ b/types/recompose/recompose-tests.tsx @@ -83,20 +83,20 @@ function testWithPropsOnChange() { } function testWithHandlers() { + interface OutterProps { + out: number; + } interface InnerProps { - onSubmit: React.MouseEventHandler; - onChange: Function; foo: string; } interface HandlerProps { onSubmit: React.MouseEventHandler; onChange: Function; } - interface OutterProps { out: number; } - const InnerComponent: React.StatelessComponent = ({onChange, onSubmit}) => -
; + const InnerComponent: React.StatelessComponent = ({onChange, onSubmit, foo}) => +
{foo}
; - const enhancer = withHandlers({ + const enhancer = withHandlers({ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: React.MouseEvent) => {}, }); @@ -108,7 +108,7 @@ function testWithHandlers() { /> ) - const enhancer2 = withHandlers((props) => ({ + const enhancer2 = withHandlers((props) => ({ onChange: (props) => (e: any) => {}, onSubmit: (props) => (e: React.MouseEvent) => {}, })); From da85335863a54209c571c55880ae732aa0c1327d Mon Sep 17 00:00:00 2001 From: Lee Henson Date: Wed, 13 Dec 2017 14:09:18 +0000 Subject: [PATCH 10/10] simplify withHandlers test --- types/recompose/recompose-tests.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/recompose/recompose-tests.tsx b/types/recompose/recompose-tests.tsx index a780fc7ded..1504ab563a 100644 --- a/types/recompose/recompose-tests.tsx +++ b/types/recompose/recompose-tests.tsx @@ -120,10 +120,10 @@ function testWithHandlers() { /> ) - const handlerNameTypecheckProof = withHandlers((props) => ({ // $ExpectError - onChange: () => () => {}, // $ExpectError + const handlerNameTypecheckProof = withHandlers({ + onChange: () => () => {}, notAKeyOnHandlerProps: () => () => {}, // $ExpectError - })); // $ExpectError + }); } function testDefaultProps() {