>(component: TargetClass): TargetClass & ConnectClass;
- }
-
- interface ConnectState {
- storeState: S
- }
-
- class Connect
extends React.Component
> {
- version: number;
- store: Redux.Store;
- state: ConnectState;
- }
-
- interface ConnectClass
extends React.ComponentClass
{
- constructor(props?: P, context?: any): Connect
;
- WrappedComponent: W;
- }
-
- export type DefaultStateProps = {};
- export type DefaultDispatchProps = { dispatch: Redux.Dispatch };
- export type DefaultMergedProps = TOwnProps & TStateProps & TDispatchProps;
-
- /**
- * 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 responsibility 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?: FuncOrSelf>,
- mapDispatchToProps?: FuncOrSelf | MapDispatchToPropsObject>,
- mergeProps?: void,
- options?: Options
- ): ComponentDecorator;
-
- export function connect(
- mapStateToProps?: FuncOrSelf>,
- mapDispatchToProps?: FuncOrSelf | MapDispatchToPropsObject>,
- mergeProps?: void,
- options?: Options
- ): ComponentDecorator, TOwnProps>;
-
- export function connect(
- mapStateToProps?: FuncOrSelf>,
- mapDispatchToProps?: FuncOrSelf | MapDispatchToPropsObject>,
- mergeProps?: MergeProps,
- options?: Options
- ): ComponentDecorator;
-
- type FuncOrSelf = (() => T) | T;
-
- type MapStateToProps = (state: any, ownProps?: TOwnProps) => TStateProps;
-
- type InferableMapStateToProps = (state: any, ownProps?: TOwnProps) => TStateProps;
-
- type MapDispatchToPropsFunction = (dispatch: Redux.Dispatch, ownProps?: TOwnProps) => TDispatchProps;
-
- type InferableMapDispatchToPropsFunction = (dispatch: Redux.Dispatch, ownProps?: TOwnProps) => TDispatchProps;
-
- type MapDispatchToPropsObject = {
- [name: string]: Redux.ActionCreator;
- }
-
- type MergeProps =
- (stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps) => TMergedProps;
-
- export interface Options {
- /**
- * If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps,
- * preventing unnecessary updates, assuming that the component is a “pure” component
- * and does not rely on any input or state other than its props and the selected Redux store’s state.
- * Defaults to true.
- * @default true
- */
- pure?: boolean;
- /**
- * If true, stores a ref to the wrapped component instance and makes it available via
- * getWrappedInstance() method. Defaults to false.
- */
- withRef?: boolean;
- }
-
- export interface ProviderProps {
- /**
- * The single Redux store in your application.
- */
- store?: Redux.Store;
- children?: React.ReactNode;
- }
-
- /**
- * Makes the Redux store available to the connect() calls in the component hierarchy below.
- */
- export class Provider extends React.Component {
- }
+interface ComponentDecorator {
+ (component: ComponentClass | StatelessComponent): 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 {
+ | StatelessComponent
)>(component: TComponentConstruct): TComponentConstruct;
+}
+
+/**
+ * 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 responsibility to extend ownProps interface from state or
+ * dispatch props or both when applicable
+ *
+ * @param mapStateToProps
+ * @param mapDispatchToProps
+ * @param mergeProps
+ * @param options
+ */
+export declare function connect(): InferableComponentDecorator;
+
+export declare function connect(
+ mapStateToProps: FuncOrSelf>,
+ mapDispatchToProps?: FuncOrSelf | MapDispatchToPropsObject>
+): ComponentDecorator;
+
+export declare function connect(
+ mapStateToProps: FuncOrSelf>,
+ mapDispatchToProps: FuncOrSelf | MapDispatchToPropsObject>,
+ mergeProps: MergeProps,
+ options?: Options
+): ComponentDecorator;
+
+type FuncOrSelf = T | (() => T);
+
+interface MapStateToProps {
+ (state: any, ownProps?: TOwnProps): TStateProps;
+}
+
+interface MapDispatchToPropsFunction {
+ (dispatch: Dispatch, ownProps?: TOwnProps): TDispatchProps;
+}
+
+interface MapDispatchToPropsObject {
+ [name: string]: ActionCreator;
+}
+
+interface MergeProps {
+ (stateProps: TStateProps, dispatchProps: TDispatchProps, ownProps: TOwnProps): TStateProps & TDispatchProps;
+}
+
+interface Options {
+ /**
+ * If true, implements shouldComponentUpdate and shallowly compares the result of mergeProps,
+ * preventing unnecessary updates, assuming that the component is a “pure” component
+ * and does not rely on any input or state other than its props and the selected Redux store’s state.
+ * Defaults to true.
+ * @default true
+ */
+ pure?: boolean;
+ /**
+ * If true, stores a ref to the wrapped component instance and makes it available via
+ * getWrappedInstance() method. Defaults to false.
+ */
+ withRef?: boolean;
+}
+
+export interface ProviderProps {
+ /**
+ * The single Redux store in your application.
+ */
+ store?: Store;
+ children?: ReactNode;
+}
+
+/**
+ * Makes the Redux store available to the connect() calls in the component hierarchy below.
+ */
+export class Provider extends React.Component { }
diff --git a/react-redux/react-redux-tests.tsx b/react-redux/react-redux-tests.tsx
index e8aac5dfb3..8c552c8272 100644
--- a/react-redux/react-redux-tests.tsx
+++ b/react-redux/react-redux-tests.tsx
@@ -8,7 +8,7 @@ import { Component, ReactElement } from 'react';
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Router, RouterState } from 'react-router';
-import { ActionCreator, Store, Dispatch, bindActionCreators } from 'redux';
+import { Store, Dispatch, bindActionCreators } from 'redux';
import { connect, Provider } from 'react-redux';
import objectAssign = require('object-assign');
import * as History from 'history';
@@ -17,324 +17,302 @@ import * as History from 'history';
// Quick Start
// https://github.com/rackt/react-redux/blob/master/docs/quick-start.md#quick-start
//
-namespace TestQuickStartExample {
- interface CounterStateProps {
- value: number;
- }
- interface CounterDispatchProps {
- onIncrement: ActionCreator;
- }
- interface CounterState {
- counter: number;
- }
- declare var increment: Function;
- class Counter extends Component {
- render() {
- return (
-
- );
- }
- }
+interface CounterState {
+ counter: number;
+}
+declare var increment: Function;
- const mapStateToProps = (state: CounterState) => ({
+class Counter extends Component {
+ render() {
+ return (
+
+ );
+ }
+}
+
+function mapStateToProps(state: CounterState) {
+ return {
value: state.counter
- });
+ };
+}
- // Which action creators does it want to receive by props?
- const mapDispatchToProps = (dispatch: Dispatch) => ({
+// Which action creators does it want to receive by props?
+function mapDispatchToProps(dispatch: Dispatch) {
+ return {
onIncrement: () => dispatch(increment())
+ };
+}
+
+connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Counter);
+
+
+@connect(mapStateToProps)
+class CounterContainer extends Component {
+
+}
+
+// Ensure connect's first two arguments can be replaced by wrapper functions
+interface ICounterStateProps {
+ value: number
+}
+interface ICounterDispatchProps {
+ onIncrement: () => void
+}
+connect(
+ () => mapStateToProps,
+ () => mapDispatchToProps
+)(Counter);
+// only first argument
+connect(
+ () => mapStateToProps
+)(Counter);
+// wrap only one argument
+connect(
+ mapStateToProps,
+ () => mapDispatchToProps
+)(Counter);
+// with extra arguments
+connect(
+ () => mapStateToProps,
+ () => mapDispatchToProps,
+ (s: ICounterStateProps, d: ICounterDispatchProps) =>
+ objectAssign({}, s, d),
+ { pure: true }
+)(Counter);
+
+
+class App extends Component {
+ render(): JSX.Element {
+ // ...
+ return null;
+ }
+}
+
+const targetEl = document.getElementById('root');
+
+ReactDOM.render((
+
+ {() => }
+
+), targetEl);
+
+//
+// API
+// https://github.com/rackt/react-redux/blob/master/docs/api.md
+//
+declare var store: Store;
+declare var routerState: RouterState;
+declare var history: History.History;
+class MyRootComponent extends Component {
+
+}
+class TodoApp extends Component {
+
+}
+interface TodoState {
+ todos: string[]|string;
+}
+interface TodoProps {
+ userId: number;
+}
+interface DispatchProps {
+ addTodo(userId: number, text: string): void;
+ action: Function;
+}
+declare var actionCreators: () => {
+ action: Function;
+}
+declare var addTodo: () => { type: string; };
+declare var todoActionCreators: { [type: string]: (...args: any[]) => any; };
+declare var counterActionCreators: { [type: string]: (...args: any[]) => any; };
+
+ReactDOM.render(
+
+ {() => }
+ ,
+ document.body
+);
+
+//TODO: for React Router 0.13
+////TODO: error TS2339: Property 'run' does not exist on type 'typeof "react-router"'.
+////TODO: error TS2339: Property 'HistoryLocation' does not exist on type 'typeof "react-router"'.
+//declare var routes: any;
+//Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // note "routerState" here
+// ReactDOM.render(
+//
+// {/*
+// //TODO: error TS2339: Property 'routerState' does not exist on type 'RouteProp'.
+// {() => } // note "routerState" here: important to pass it down
+// */}
+// ,
+// document.getElementById('root')
+// );
+//});
+
+
+//TODO: for React Router 1.0
+ReactDOM.render(
+
+ {() => ...}
+ ,
+ targetEl
+);
+
+// Inject just dispatch and don't listen to store
+
+connect()(TodoApp);
+
+// Inject dispatch and every field in the global state
+
+connect((state: TodoState) => state)(TodoApp);
+
+// Inject dispatch and todos
+
+function mapStateToProps2(state: TodoState) {
+ return { todos: state.todos };
+}
+
+export default connect(mapStateToProps2)(TodoApp);
+
+// Inject todos and all action creators (addTodo, completeTodo, ...)
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+connect(mapStateToProps2, actionCreators)(TodoApp);
+
+// Inject todos and all action creators (addTodo, completeTodo, ...) as actions
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mapDispatchToProps2(dispatch: Dispatch) {
+ return { actions: bindActionCreators(actionCreators, dispatch) };
+}
+
+connect(mapStateToProps2, mapDispatchToProps2)(TodoApp);
+
+// Inject todos and a specific action creator (addTodo)
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mapDispatchToProps3(dispatch: Dispatch) {
+ return bindActionCreators({ addTodo }, dispatch);
+}
+
+connect(mapStateToProps2, mapDispatchToProps3)(TodoApp);
+
+// Inject todos, todoActionCreators as todoActions, and counterActionCreators as counterActions
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mapDispatchToProps4(dispatch: Dispatch) {
+ return {
+ todoActions: bindActionCreators(todoActionCreators, dispatch),
+ counterActions: bindActionCreators(counterActionCreators, dispatch)
+ };
+}
+
+connect(mapStateToProps2, mapDispatchToProps4)(TodoApp);
+
+// Inject todos, and todoActionCreators and counterActionCreators together as actions
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mapDispatchToProps5(dispatch: Dispatch) {
+ return {
+ actions: bindActionCreators(objectAssign({}, todoActionCreators, counterActionCreators), dispatch)
+ };
+}
+
+connect(mapStateToProps2, mapDispatchToProps5)(TodoApp);
+
+// Inject todos, and all todoActionCreators and counterActionCreators directly as props
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mapDispatchToProps6(dispatch: Dispatch) {
+ return bindActionCreators(objectAssign({}, todoActionCreators, counterActionCreators), dispatch);
+}
+
+connect(mapStateToProps2, mapDispatchToProps6)(TodoApp);
+
+// Inject todos of a specific user depending on props
+
+function mapStateToProps3(state: TodoState, ownProps: TodoProps): TodoState {
+ return { todos: state.todos[ownProps.userId] };
+}
+
+connect(mapStateToProps3)(TodoApp);
+
+// Inject todos of a specific user depending on props, and inject props.userId into the action
+
+//function mapStateToProps(state) {
+// return { todos: state.todos };
+//}
+
+function mergeProps(stateProps: TodoState, dispatchProps: DispatchProps, ownProps: TodoProps): DispatchProps & TodoState & TodoProps {
+ return objectAssign({}, ownProps, dispatchProps, {
+ todos: stateProps.todos[ownProps.userId],
+ addTodo: (text: string) => dispatchProps.addTodo(ownProps.userId, text)
});
-
- connect(
- mapStateToProps,
- mapDispatchToProps
- )(Counter);
-
- // https://github.com/Microsoft/TypeScript/issues/4881
- //@connect(mapStateToProps)
- //class WrappedCounter extends Component {
- //}
- //React.createElement(WrappedCounter, {});
- // As a workaround, declare state and dispatch props as optional
- interface OptionalCounterStateProps {
- value?: number;
- }
- interface OptionalCounterDispatchProps {
- onIncrement?: ActionCreator;
- }
- interface Props {
- requiredOwnProp: string
- }
- @connect(mapStateToProps)
- class WrappedCounter extends Component {
- }
- React.createElement(WrappedCounter, { requiredOwnProp: 'here I am' });
-
- // Ensure connect's first two arguments can be replaced by wrapper functions
- connect(
- () => mapStateToProps,
- () => mapDispatchToProps
- )(Counter);
- // only first argument
- connect(
- () => mapStateToProps
- )(Counter);
- // wrap only one argument
- connect(
- mapStateToProps,
- () => mapDispatchToProps
- )(Counter);
- // with extra arguments
- connect(
- () => mapStateToProps,
- () => mapDispatchToProps,
- (stateProps: CounterStateProps, dispatchProps: CounterDispatchProps) =>
- objectAssign({}, stateProps, dispatchProps),
- { pure: true }
- )(Counter);
}
-namespace TestTodosApp {
-
- class App extends Component {
- render(): JSX.Element {
- // ...
- return null;
- }
- }
-
- const targetEl = document.getElementById('root');
-
- ReactDOM.render((
-
- {() => }
-
- ), targetEl);
-
- //
- // API
- // https://github.com/rackt/react-redux/blob/master/docs/api.md
- //
- declare var store: Store;
- declare var routerState: RouterState;
- declare var history: History.History;
- class MyRootComponent extends Component<{}, RouterState> {
- }
- class TodoApp extends Component {
- }
- interface TodoState {
- todos: string[] | string;
- }
- interface TodoProps {
- userId: number;
- }
- interface DispatchProps {
- addTodo(userId: number, text: string): void;
- action: Function;
- }
- declare var actionCreators: () => {
- action: Function;
- }
- declare var addTodo: () => { type: string; };
- declare var todoActionCreators: { [type: string]: (...args: any[]) => any; };
- declare var counterActionCreators: { [type: string]: (...args: any[]) => any; };
-
- ReactDOM.render(
-
- {() => }
- ,
- document.body
- );
-
- //TODO: for React Router 0.13
- ////TODO: error TS2339: Property 'run' does not exist on type 'typeof "react-router"'.
- ////TODO: error TS2339: Property 'HistoryLocation' does not exist on type 'typeof "react-router"'.
- //declare var routes: any;
- //Router.run(routes, Router.HistoryLocation, (Handler, routerState) => { // note "routerState" here
- // ReactDOM.render(
- //
- // {/*
- // //TODO: error TS2339: Property 'routerState' does not exist on type 'RouteProp'.
- // {() => } // note "routerState" here: important to pass it down
- // */}
- // ,
- // document.getElementById('root')
- // );
- //});
+connect(mapStateToProps2, actionCreators, mergeProps)(TodoApp);
- //TODO: for React Router 1.0
- ReactDOM.render(
-
- {() => ...}
- ,
- targetEl
- );
- // Inject just dispatch and don't listen to store
- connect()(TodoApp);
- // Inject dispatch and every field in the global state
-
- connect((state: TodoState) => state)(TodoApp);
-
- // Inject dispatch and todos
-
- function mapStateToProps(state: TodoState) {
- return { todos: state.todos };
- }
-
- connect(mapStateToProps)(TodoApp);
-
- // Inject todos and all action creators (addTodo, completeTodo, ...)
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- connect(mapStateToProps, actionCreators)(TodoApp);
-
- // Inject todos and all action creators (addTodo, completeTodo, ...) as actions
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mapDispatchToProps(dispatch: Dispatch) {
- return { actions: bindActionCreators(actionCreators, dispatch) };
- }
-
- connect(mapStateToProps, mapDispatchToProps)(TodoApp);
-
- // Inject todos and a specific action creator (addTodo)
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mapDispatchToProps2(dispatch: Dispatch) {
- return bindActionCreators({ addTodo }, dispatch);
- }
-
- connect(mapStateToProps, mapDispatchToProps2)(TodoApp);
-
- // Inject todos, todoActionCreators as todoActions, and counterActionCreators as counterActions
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mapDispatchToProps3(dispatch: Dispatch) {
- return {
- todoActions: bindActionCreators(todoActionCreators, dispatch),
- counterActions: bindActionCreators(counterActionCreators, dispatch)
- };
- }
-
- connect(mapStateToProps, mapDispatchToProps3)(TodoApp);
-
- // Inject todos, and todoActionCreators and counterActionCreators together as actions
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mapDispatchToProps4(dispatch: Dispatch) {
- return {
- actions: bindActionCreators(objectAssign({}, todoActionCreators, counterActionCreators), dispatch)
- };
- }
-
- connect(mapStateToProps, mapDispatchToProps4)(TodoApp);
-
- // Inject todos, and all todoActionCreators and counterActionCreators directly as props
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mapDispatchToProps5(dispatch: Dispatch) {
- return bindActionCreators(objectAssign({}, todoActionCreators, counterActionCreators), dispatch);
- }
-
- connect(mapStateToProps, mapDispatchToProps5)(TodoApp);
-
- // Inject todos of a specific user depending on props
-
- function mapStateToProps2(state: TodoState, ownProps: TodoProps): TodoState {
- return { todos: state.todos[ownProps.userId] };
- }
-
- connect(mapStateToProps2)(TodoApp);
-
- // Inject todos of a specific user depending on props, and inject props.userId into the action
-
- //function mapStateToProps(state) {
- // return { todos: state.todos };
- //}
-
- function mergeProps(stateProps: TodoState, dispatchProps: DispatchProps, ownProps: TodoProps): DispatchProps & TodoState & TodoProps {
- return objectAssign({}, ownProps, dispatchProps, {
- todos: stateProps.todos[ownProps.userId],
- addTodo: (text: string) => dispatchProps.addTodo(ownProps.userId, text)
- });
- }
-
- connect(mapStateToProps, actionCreators, mergeProps)(TodoApp);
+interface TestProp {
+ property1: number;
+ someOtherProperty?: string;
}
-
-namespace TestComponent {
- declare var store: Store;
- interface TestProps {
- property1: number;
- someOtherProperty?: string;
- }
- interface TestState {
- isLoaded: boolean;
- state1: number;
- }
- class TestComponent extends Component {
- static staticMethod(): number {
- return 0;
- }
- }
- // Own properties can't be always be inferred, so a single type parameter will do
- const WrappedTestComponent = connect()(TestComponent);
-
- // return value of the connect()(TestComponent) is of the type TestComponent
- let ATestComponent: typeof TestComponent = null;
- ATestComponent = TestComponent;
- ATestComponent = WrappedTestComponent;
- // Wrapped component is accessible
- WrappedTestComponent.WrappedComponent === TestComponent
- // Static properties are accessible
- WrappedTestComponent.staticMethod();
-
- ;
- ;
- ;
-
- class NonComponent {
- }
- // this doesn't compile
- //connect()(NonComponent);
-
- // stateless functions
- interface HelloMessageProps {
- name: string;
- }
- function HelloMessage(props: HelloMessageProps) {
- return Hello {props.name}
;
- }
-
- const ConnectedHelloMessage = connect()(HelloMessage);
- ReactDOM.render(, document.getElementById('content'));
- ReactDOM.render(, document.getElementById('content'));
+interface TestState {
+ isLoaded: boolean;
+ state1: number;
}
+class TestComponent extends Component { }
+const WrappedTestComponent = connect()(TestComponent);
+
+// return value of the connect()(TestComponent) is of the type TestComponent
+let ATestComponent: typeof TestComponent = null;
+ATestComponent = TestComponent;
+ATestComponent = WrappedTestComponent;
+
+let anElement: ReactElement;
+;
+;
+;
+
+class NonComponent {}
+// this doesn't compile
+//connect()(NonComponent);
+
+// stateless functions
+interface HelloMessageProps { name: string; }
+function HelloMessage(props: HelloMessageProps) {
+ return Hello {props.name}
;
+}
+let ConnectedHelloMessage = connect()(HelloMessage);
+ReactDOM.render(, document.getElementById('content'));
+ReactDOM.render(, document.getElementById('content'));
// stateless functions that uses mapStateToProps and mapDispatchToProps
namespace TestStatelessFunctionWithMapArguments {
@@ -365,8 +343,6 @@ namespace TestStatelessFunctionWithMapArguments {
mapStateToProps,
mapDispatchToProps
)(Greeting);
-
- React.createElement(ConnectedGreeting, { name: 'hello', onClick: () => undefined });
}
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/8787
@@ -398,7 +374,7 @@ namespace TestTOwnPropsInference {
const ConnectedWithTypeHint = connect(mapStateToPropsWithoutOwnProps)(OwnPropsComponent);
// This compiles, which is bad.
- // React.createElement(ConnectedWithoutOwnProps, { anything: 'goes!' });
+ React.createElement(ConnectedWithoutOwnProps, { anything: 'goes!' });
// This compiles, as expected.
React.createElement(ConnectedWithOwnProps, { own: 'string' });