diff --git a/rollup.config.js b/rollup.config.js
index f75eb672d..2f8d004ff 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -10,7 +10,7 @@ const env = process.env.NODE_ENV
const extensions = ['.js', '.ts', '.tsx', '.json']
const config = {
- input: 'src/index.js',
+ input: 'src/index.ts',
external: Object.keys(pkg.peerDependencies || {}).concat('react-dom'),
output: {
format: 'umd',
diff --git a/src/components/Provider.tsx b/src/components/Provider.tsx
index 32f100994..90a04b83e 100644
--- a/src/components/Provider.tsx
+++ b/src/components/Provider.tsx
@@ -5,7 +5,7 @@ import { useIsomorphicLayoutEffect } from '../utils/useIsomorphicLayoutEffect'
import type { FixTypeLater } from '../types'
import { Action, AnyAction, Store } from 'redux'
-interface ProviderProps {
+export interface ProviderProps {
/**
* The single Redux store in your application.
*/
diff --git a/src/components/connectAdvanced.tsx b/src/components/connectAdvanced.tsx
index cd645cc91..56bcd65e2 100644
--- a/src/components/connectAdvanced.tsx
+++ b/src/components/connectAdvanced.tsx
@@ -173,14 +173,7 @@ export interface ConnectProps {
store?: Store
}
-export type ConnectedComponent<
- C extends React.ComponentType,
- P
-> = React.NamedExoticComponent> & {
- WrappedComponent: C
-}
-
-interface ConnectAdvancedOptions {
+export interface ConnectAdvancedOptions {
getDisplayName?: (name: string) => string
methodName?: string
shouldHandleStateChanges?: boolean
@@ -189,7 +182,16 @@ interface ConnectAdvancedOptions {
pure?: boolean
}
-export default function connectAdvanced(
+interface AnyObject {
+ [x: string]: any
+}
+
+export default function connectAdvanced<
+ S,
+ TProps,
+ TOwnProps,
+ TFactoryOptions extends AnyObject = {}
+>(
/*
selectorFactory is a func that is responsible for returning the selector function used to
compute new props from state, props, and dispatch. For example:
@@ -207,7 +209,7 @@ export default function connectAdvanced(
props. Do not use connectAdvanced directly without memoizing results between calls to your
selector, otherwise the Connect component will re-render on every state or props change.
*/
- selectorFactory: SelectorFactory,
+ selectorFactory: SelectorFactory,
// options object:
{
// the func used to compute this HOC's displayName from the wrapped component's displayName.
@@ -229,7 +231,7 @@ export default function connectAdvanced(
// additional options are passed through to the selectorFactory
...connectOptions
- }: ConnectAdvancedOptions = {}
+ }: ConnectAdvancedOptions & Partial = {}
) {
const Context = context
diff --git a/src/connect/connect.ts b/src/connect/connect.ts
index c341ddb2c..d83513b2f 100644
--- a/src/connect/connect.ts
+++ b/src/connect/connect.ts
@@ -1,5 +1,6 @@
import type { Dispatch } from 'redux'
import connectAdvanced from '../components/connectAdvanced'
+import type { ConnectAdvancedOptions } from '../components/connectAdvanced'
import shallowEqual from '../utils/shallowEqual'
import defaultMapDispatchToPropsFactories from './mapDispatchToProps'
import defaultMapStateToPropsFactories from './mapStateToProps'
@@ -9,6 +10,7 @@ import defaultSelectorFactory, {
MapDispatchToPropsParam,
MergeProps,
} from './selectorFactory'
+import type { DefaultRootState } from '../types'
/*
connect is a facade over connectAdvanced. It turns its args into a compatible
@@ -50,6 +52,31 @@ function strictEqual(a: unknown, b: unknown) {
return a === b
}
+export interface ConnectOptions<
+ State = DefaultRootState,
+ TStateProps = {},
+ TOwnProps = {},
+ TMergedProps = {}
+> extends ConnectAdvancedOptions {
+ pure?: boolean | undefined
+ areStatesEqual?: ((nextState: State, prevState: State) => boolean) | undefined
+
+ areOwnPropsEqual?: (
+ nextOwnProps: TOwnProps,
+ prevOwnProps: TOwnProps
+ ) => boolean
+
+ areStatePropsEqual?: (
+ nextStateProps: TStateProps,
+ prevStateProps: TStateProps
+ ) => boolean
+ areMergedPropsEqual?: (
+ nextMergedProps: TMergedProps,
+ prevMergedProps: TMergedProps
+ ) => boolean
+ forwardRef?: boolean | undefined
+}
+
// createConnect with default args builds the 'official' connect behavior. Calling it with
// different options opens up some testing and extensibility scenarios
export function createConnect({
@@ -70,7 +97,7 @@ export function createConnect({
areStatePropsEqual = shallowEqual,
areMergedPropsEqual = shallowEqual,
...extraOptions
- } = {}
+ }: ConnectOptions = {}
) {
const initMapStateToProps = match(
mapStateToProps,
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index 27254b6e8..000000000
--- a/src/index.js
+++ /dev/null
@@ -1,29 +0,0 @@
-import Provider from './components/Provider'
-import connectAdvanced from './components/connectAdvanced'
-import { ReactReduxContext } from './components/Context'
-import connect from './connect/connect'
-
-import { useDispatch, createDispatchHook } from './hooks/useDispatch'
-import { useSelector, createSelectorHook } from './hooks/useSelector'
-import { useStore, createStoreHook } from './hooks/useStore'
-
-import { setBatch } from './utils/batch'
-import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
-import shallowEqual from './utils/shallowEqual'
-
-setBatch(batch)
-
-export {
- Provider,
- connectAdvanced,
- ReactReduxContext,
- connect,
- batch,
- useDispatch,
- createDispatchHook,
- useSelector,
- createSelectorHook,
- useStore,
- createStoreHook,
- shallowEqual,
-}
diff --git a/src/index.ts b/src/index.ts
new file mode 100644
index 000000000..1073f81e2
--- /dev/null
+++ b/src/index.ts
@@ -0,0 +1,66 @@
+import Provider from './components/Provider'
+import type { ProviderProps } from './components/Provider'
+import connectAdvanced from './components/connectAdvanced'
+import type {
+ ConnectAdvancedOptions,
+ ConnectProps,
+} from './components/connectAdvanced'
+import type {
+ SelectorFactory,
+ Selector,
+ MapStateToProps,
+ MapStateToPropsFactory,
+ MapStateToPropsParam,
+ MapDispatchToPropsFunction,
+ MapDispatchToProps,
+ MapDispatchToPropsFactory,
+ MapDispatchToPropsParam,
+ MapDispatchToPropsNonObject,
+ MergeProps,
+} from './connect/selectorFactory'
+import { ReactReduxContext } from './components/Context'
+import type { ReactReduxContextValue } from './components/Context'
+import connect from './connect/connect'
+
+import { useDispatch, createDispatchHook } from './hooks/useDispatch'
+import { useSelector, createSelectorHook } from './hooks/useSelector'
+import { useStore, createStoreHook } from './hooks/useStore'
+
+import { setBatch } from './utils/batch'
+import { unstable_batchedUpdates as batch } from './utils/reactBatchedUpdates'
+import shallowEqual from './utils/shallowEqual'
+
+setBatch(batch)
+
+export * from './types'
+export type {
+ ProviderProps,
+ SelectorFactory,
+ Selector,
+ MapStateToProps,
+ MapStateToPropsFactory,
+ MapStateToPropsParam,
+ ConnectProps,
+ ConnectAdvancedOptions,
+ MapDispatchToPropsFunction,
+ MapDispatchToProps,
+ MapDispatchToPropsFactory,
+ MapDispatchToPropsParam,
+ MapDispatchToPropsNonObject,
+ MergeProps,
+ ReactReduxContextValue,
+}
+export {
+ Provider,
+ connectAdvanced,
+ ReactReduxContext,
+ connect,
+ batch,
+ useDispatch,
+ createDispatchHook,
+ useSelector,
+ createSelectorHook,
+ useStore,
+ createStoreHook,
+ shallowEqual,
+}
diff --git a/src/types.ts b/src/types.ts
index 6b436517d..dc2a91f48 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -256,3 +256,22 @@ export type ResolveArrayThunks> =
: TDispatchProps extends ReadonlyArray
? ReadonlyArray>
: never
+
+/**
+ * This interface allows you to easily create a hook that is properly typed for your
+ * store's root state.
+ *
+ * @example
+ *
+ * interface RootState {
+ * property: string;
+ * }
+ *
+ * const useTypedSelector: TypedUseSelectorHook = useSelector;
+ */
+export interface TypedUseSelectorHook {
+ (
+ selector: (state: TState) => TSelected,
+ equalityFn?: (left: TSelected, right: TSelected) => boolean
+ ): TSelected
+}
diff --git a/test/components/Provider.spec.js b/test/components/Provider.spec.js
index f4a3987df..116570a1e 100644
--- a/test/components/Provider.spec.js
+++ b/test/components/Provider.spec.js
@@ -3,11 +3,14 @@
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import { createStore } from 'redux'
-import { Provider, connect, ReactReduxContext } from '../../src/index.js'
+import { Provider, connect, ReactReduxContext } from '../../src/index'
import * as rtl from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
-const createExampleTextReducer = () => (state = 'example text') => state
+const createExampleTextReducer =
+ () =>
+ (state = 'example text') =>
+ state
describe('React', () => {
describe('Provider', () => {
diff --git a/test/components/connect.spec.js b/test/components/connect.spec.js
index 212b0a8ec..765596794 100644
--- a/test/components/connect.spec.js
+++ b/test/components/connect.spec.js
@@ -5,7 +5,7 @@ import createClass from 'create-react-class'
import PropTypes from 'prop-types'
import ReactDOM from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
-import { Provider as ProviderMock, connect } from '../../src/index.js'
+import { Provider as ProviderMock, connect } from '../../src/index'
import * as rtl from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
diff --git a/test/components/connectAdvanced.spec.js b/test/components/connectAdvanced.spec.js
index 4b0f8bdab..4df5df798 100644
--- a/test/components/connectAdvanced.spec.js
+++ b/test/components/connectAdvanced.spec.js
@@ -1,6 +1,6 @@
import React, { Component } from 'react'
import * as rtl from '@testing-library/react'
-import { Provider as ProviderMock, connectAdvanced } from '../../src/index.js'
+import { Provider as ProviderMock, connectAdvanced } from '../../src/index'
import { createStore } from 'redux'
import '@testing-library/jest-dom/extend-expect'
diff --git a/test/components/hooks.spec.js b/test/components/hooks.spec.js
index 44a797210..ece7250ea 100644
--- a/test/components/hooks.spec.js
+++ b/test/components/hooks.spec.js
@@ -2,7 +2,7 @@
import React from 'react'
import { createStore } from 'redux'
-import { Provider as ProviderMock, connect } from '../../src/index.js'
+import { Provider as ProviderMock, connect } from '../../src/index'
import * as rtl from '@testing-library/react'
import '@testing-library/jest-dom/extend-expect'
diff --git a/test/hooks/useDispatch.spec.js b/test/hooks/useDispatch.spec.js
index de3dbe46e..9473cc791 100644
--- a/test/hooks/useDispatch.spec.js
+++ b/test/hooks/useDispatch.spec.js
@@ -5,7 +5,7 @@ import {
Provider as ProviderMock,
useDispatch,
createDispatchHook,
-} from '../../src/index.js'
+} from '../../src/index'
const store = createStore((c) => c + 1)
const store2 = createStore((c) => c + 2)
diff --git a/test/hooks/useSelector.spec.js b/test/hooks/useSelector.spec.js
index 2f927214e..ab2a29738 100644
--- a/test/hooks/useSelector.spec.js
+++ b/test/hooks/useSelector.spec.js
@@ -10,7 +10,7 @@ import {
shallowEqual,
connect,
createSelectorHook,
-} from '../../src/index.js'
+} from '../../src/index'
import { useReduxContext } from '../../src/hooks/useReduxContext'
describe('React', () => {
diff --git a/test/integration/dynamic-reducers.spec.js b/test/integration/dynamic-reducers.spec.js
index 841b0377a..dc3e8addf 100644
--- a/test/integration/dynamic-reducers.spec.js
+++ b/test/integration/dynamic-reducers.spec.js
@@ -3,7 +3,7 @@
import React from 'react'
import ReactDOMServer from 'react-dom/server'
import { createStore, combineReducers } from 'redux'
-import { connect, Provider, ReactReduxContext } from '../../src/index.js'
+import { connect, Provider, ReactReduxContext } from '../../src/index'
import * as rtl from '@testing-library/react'
describe('React', () => {
diff --git a/test/integration/server-rendering.spec.js b/test/integration/server-rendering.spec.js
index a957907f7..7d92ebf99 100644
--- a/test/integration/server-rendering.spec.js
+++ b/test/integration/server-rendering.spec.js
@@ -11,7 +11,7 @@
import React from 'react'
import { renderToString } from 'react-dom/server'
import { createStore } from 'redux'
-import { Provider, connect } from '../../src/index.js'
+import { Provider, connect } from '../../src/index'
describe('React', () => {
describe('server rendering', () => {
diff --git a/test/react-native/batch-integration.js b/test/react-native/batch-integration.js
index e5b8586d4..bc28f51ab 100644
--- a/test/react-native/batch-integration.js
+++ b/test/react-native/batch-integration.js
@@ -7,7 +7,7 @@ import {
batch,
useSelector,
useDispatch,
-} from '../../src/index.js'
+} from '../../src/index'
import { useIsomorphicLayoutEffect } from '../../src/utils/useIsomorphicLayoutEffect'
import * as rtl from '@testing-library/react-native'
import '@testing-library/jest-native/extend-expect'
@@ -468,9 +468,8 @@ describe('React Native', () => {
const rendered = rtl.render()
const assertValuesMatch = (rendered) => {
- const [, boolFromSelector] = rendered.getByTestId(
- 'boolFromSelector'
- ).children
+ const [, boolFromSelector] =
+ rendered.getByTestId('boolFromSelector').children
const [, boolFromStore] = rendered.getByTestId('boolFromStore').children
expect(boolFromSelector).toBe(boolFromStore)
}