From 206c964ed1f68ec1778c029123dada84f2aa25c1 Mon Sep 17 00:00:00 2001 From: "Ciuca, Alexandru" Date: Wed, 2 Mar 2016 19:41:29 +0200 Subject: [PATCH 1/2] react-redux: stricter typing of connect --- react-redux/react-redux-tests.tsx | 9 +---- react-redux/react-redux.d.ts | 64 ++++++++++++++++++++++--------- 2 files changed, 47 insertions(+), 26 deletions(-) diff --git a/react-redux/react-redux-tests.tsx b/react-redux/react-redux-tests.tsx index 3254e6ec73..9528a7aca0 100644 --- a/react-redux/react-redux-tests.tsx +++ b/react-redux/react-redux-tests.tsx @@ -94,6 +94,7 @@ interface TodoProps { } interface DispatchProps { addTodo(userId: number, text: string): void; + action: Function; } declare var actionCreators: () => { action: Function; @@ -270,11 +271,3 @@ let anElement: ReactElement; class NonComponent {} // this doesn't compile //connect()(NonComponent); - -// connect()(SomeClass) has the same constructor as SomeClass itself -class SomeClass extends Component { - constructor(public foo: string) { super() } - public bar: number; -} -let bar: number = new (connect()(SomeClass))("foo").bar; - diff --git a/react-redux/react-redux.d.ts b/react-redux/react-redux.d.ts index 767b6b1097..a052daada3 100644 --- a/react-redux/react-redux.d.ts +++ b/react-redux/react-redux.d.ts @@ -7,40 +7,68 @@ /// declare module "react-redux" { - import { Component } from 'react'; + import { Component, ComponentClass, Props, ReactNode } from 'react'; import { Store, Dispatch, ActionCreator } from 'redux'; - export class ElementClass extends Component { } - export interface ClassDecorator { - (component: T): T + /** Generic decorator, that receives T = original props, U = own props */ + interface ComponentDecorator, U extends Props> { + (component: ComponentClass): ComponentClass; + } + + /** + * Decorator that infers the type from the original component + * + * Can't use the above decorator because it would default the type to {} + */ + export interface InferableComponentDecorator { +

(component: ComponentClass

): ComponentClass

; } /** * Connects a React component to a Redux store. + * + * - Without arguments, just wraps the component, without changing the behavior / props + * + * - If 2 params are passed (3rd param, mergeProps, is skipped), default behavior + * is to override ownProps (as stated in the docs), so what remains is everything that's + * not a state or dispatch prop + * + * - When 3rd param is passed, we don't know if ownProps propagate and whether they + * should be valid component props, because it depends on mergeProps implementation. + * As such, it is the user's responsability to extend ownProps interface from state or + * dispatch props or both when applicable + * * @param mapStateToProps * @param mapDispatchToProps * @param mergeProps * @param options - */ - export function connect(mapStateToProps?: MapStateToProps, - mapDispatchToProps?: MapDispatchToPropsFunction|MapDispatchToPropsObject, - mergeProps?: MergeProps, - options?: Options): ClassDecorator; + */ + export function connect(): InferableComponentDecorator; + export function connect, U extends Props, V extends Props>( + mapStateToProps: MapStateToProps, + mapDispatchToProps?: MapDispatchToPropsFunction|MapDispatchToPropsObject + ): ComponentDecorator; + export function connect, U extends Props, V extends Props>( + mapStateToProps: MapStateToProps, + mapDispatchToProps: MapDispatchToPropsFunction|MapDispatchToPropsObject, + mergeProps: MergeProps, + options?: Options + ): ComponentDecorator; - interface MapStateToProps { - (state: any, ownProps?: any): any; + interface MapStateToProps { + (state: any, ownProps?: V): T; } - interface MapDispatchToPropsFunction { - (dispatch: Dispatch, ownProps?: any): any; + interface MapDispatchToPropsFunction { + (dispatch: Dispatch, ownProps?: V): U; } interface MapDispatchToPropsObject { [name: string]: ActionCreator; } - interface MergeProps { - (stateProps: any, dispatchProps: any, ownProps: any): any; + interface MergeProps { + (stateProps: T, dispatchProps: U, ownProps: V): T & U; } interface Options { @@ -54,16 +82,16 @@ declare module "react-redux" { pure: boolean; } - export interface Property { + export interface ProviderProps extends Props { /** * The single Redux store in your application. */ store?: Store; - children?: Function; + children?: ReactNode; } /** * Makes the Redux store available to the connect() calls in the component hierarchy below. */ - export class Provider extends Component { } + export class Provider extends Component { } } From 2fd6eb579e2afb815b5595d65e33495ea603dc73 Mon Sep 17 00:00:00 2001 From: "Ciuca, Alexandru" Date: Mon, 14 Mar 2016 14:02:11 +0200 Subject: [PATCH 2/2] Renamed type parameters; fixed typo. --- react-redux/react-redux.d.ts | 45 +++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/react-redux/react-redux.d.ts b/react-redux/react-redux.d.ts index f510767206..9bb8f7d820 100644 --- a/react-redux/react-redux.d.ts +++ b/react-redux/react-redux.d.ts @@ -10,9 +10,8 @@ declare module "react-redux" { import { ComponentClass, Component, StatelessComponent, Props, ReactNode } from 'react'; import { Store, Dispatch, ActionCreator } from 'redux'; - /** Generic decorator, that receives T = original props, U = own props */ - interface ComponentDecorator, U extends Props> { - (component: ComponentClass): ComponentClass; + interface ComponentDecorator, TOwnProps extends Props> { + (component: ComponentClass): ComponentClass; } /** @@ -35,7 +34,7 @@ declare module "react-redux" { * * - When 3rd param is passed, we don't know if ownProps propagate and whether they * should be valid component props, because it depends on mergeProps implementation. - * As such, it is the user's responsability to extend ownProps interface from state or + * As such, it is the user's responsibility to extend ownProps interface from state or * dispatch props or both when applicable * * @param mapStateToProps @@ -44,31 +43,39 @@ declare module "react-redux" { * @param options */ export function connect(): InferableComponentDecorator; - export function connect, U extends Props, V extends Props>( - mapStateToProps: MapStateToProps, - mapDispatchToProps?: MapDispatchToPropsFunction|MapDispatchToPropsObject - ): ComponentDecorator; - export function connect, U extends Props, V extends Props>( - mapStateToProps: MapStateToProps, - mapDispatchToProps: MapDispatchToPropsFunction|MapDispatchToPropsObject, - mergeProps: MergeProps, + export function connect< + TStateProps extends Props, + TDispatchProps extends Props, + TOwnProps extends Props + >( + mapStateToProps: MapStateToProps, + mapDispatchToProps?: MapDispatchToPropsFunction|MapDispatchToPropsObject + ): ComponentDecorator; + export function connect< + TStateProps extends Props, + TDispatchProps extends Props, + TOwnProps extends Props + >( + mapStateToProps: MapStateToProps, + mapDispatchToProps: MapDispatchToPropsFunction|MapDispatchToPropsObject, + mergeProps: MergeProps, options?: Options - ): ComponentDecorator; + ): ComponentDecorator; - interface MapStateToProps { - (state: any, ownProps?: V): T; + interface MapStateToProps { + (state: any, ownProps?: TOwnProps): TStateProps; } - interface MapDispatchToPropsFunction { - (dispatch: Dispatch, ownProps?: V): U; + interface MapDispatchToPropsFunction { + (dispatch: Dispatch, ownProps?: TOwnProps): TDispatchProps; } interface MapDispatchToPropsObject { [name: string]: ActionCreator; } - interface MergeProps { - (stateProps: T, dispatchProps: U, ownProps: V): T & U; + interface MergeProps { + (stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps): TStateProps & TDispatchProps; } interface Options {