;
-}
+declare namespace ReactRedux {
-/**
- * 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;
-}
+ type ComponentType
= React.ComponentClass
| React.StatelessComponent
;
-/**
- * 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;
+ interface ComponentDecorator {
+ >(component: TargetClass): TargetClass & ConnectClass;
+ }
-export declare function connect(
- mapStateToProps: FuncOrSelf>,
- mapDispatchToProps?: FuncOrSelf | MapDispatchToPropsObject>
-): ComponentDecorator;
+ interface ConnectState {
+ storeState: S
+ }
-export declare function connect(
- mapStateToProps: FuncOrSelf>,
- mapDispatchToProps: FuncOrSelf | MapDispatchToPropsObject>,
- mergeProps: MergeProps,
- options?: Options
-): ComponentDecorator;
+ class Connect extends React.Component
> {
+ version: number;
+ store: Redux.Store;
+ state: ConnectState;
+ }
-type FuncOrSelf = T | (() => T);
+ interface ConnectClass extends React.ComponentClass
{
+ constructor(props?: P, context?: any): Connect
;
+ WrappedComponent: W;
+ }
-interface MapStateToProps {
- (state: any, ownProps?: TOwnProps): TStateProps;
-}
+ export type DefaultStateProps = {};
+ export type DefaultDispatchProps = { dispatch: Redux.Dispatch };
+ export type DefaultMergedProps = TOwnProps & TStateProps & TDispatchProps;
-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
+ * 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
*/
- 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 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;
+ }
-export interface ProviderProps {
/**
- * The single Redux store in your application.
+ * Makes the Redux store available to the connect() calls in the component hierarchy below.
*/
- store?: Store;
- children?: ReactNode;
+ export class Provider extends React.Component {
+ }
}
-
-/**
- * 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 8c552c8272..e8aac5dfb3 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 { Store, Dispatch, bindActionCreators } from 'redux';
+import { ActionCreator, Store, Dispatch, bindActionCreators } from 'redux';
import { connect, Provider } from 'react-redux';
import objectAssign = require('object-assign');
import * as History from 'history';
@@ -17,302 +17,324 @@ import * as History from 'history';
// Quick Start
// https://github.com/rackt/react-redux/blob/master/docs/quick-start.md#quick-start
//
-
-interface CounterState {
- counter: number;
-}
-declare var increment: Function;
-
-class Counter extends Component {
- render() {
- return (
-
- );
+namespace TestQuickStartExample {
+ interface CounterStateProps {
+ value: number;
}
-}
+ interface CounterDispatchProps {
+ onIncrement: ActionCreator;
+ }
+ interface CounterState {
+ counter: number;
+ }
+ declare var increment: Function;
-function mapStateToProps(state: CounterState) {
- return {
+ class Counter extends Component {
+ render() {
+ return (
+
+ );
+ }
+ }
+
+ const mapStateToProps = (state: CounterState) => ({
value: state.counter
- };
-}
-
-// 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)
});
+
+ // Which action creators does it want to receive by props?
+ const mapDispatchToProps = (dispatch: Dispatch) => ({
+ onIncrement: () => dispatch(increment())
+ });
+
+ 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);
}
-connect(mapStateToProps2, actionCreators, mergeProps)(TodoApp);
+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')
+ // );
+ //});
+ //TODO: for React Router 1.0
+ ReactDOM.render(
+
+ {() => ...}
+ ,
+ targetEl
+ );
+ // Inject just dispatch and don't listen to store
+ connect()(TodoApp);
-interface TestProp {
- property1: number;
- someOtherProperty?: string;
+ // 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 TestState {
- isLoaded: boolean;
- state1: number;
+
+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'));
}
-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 {
@@ -343,6 +365,8 @@ namespace TestStatelessFunctionWithMapArguments {
mapStateToProps,
mapDispatchToProps
)(Greeting);
+
+ React.createElement(ConnectedGreeting, { name: 'hello', onClick: () => undefined });
}
// https://github.com/DefinitelyTyped/DefinitelyTyped/issues/8787
@@ -374,7 +398,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' });