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<A extends Action = AnyAction> {
+export interface ProviderProps<A extends Action = AnyAction> {
   /**
    * 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<any>,
-  P
-> = React.NamedExoticComponent<JSX.LibraryManagedAttributes<C, P>> & {
-  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<unknown, unknown, unknown, unknown>,
+  selectorFactory: SelectorFactory<S, TProps, unknown, unknown>,
   // 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<TFactoryOptions> = {}
 ) {
   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<any>> =
     : TDispatchProps extends ReadonlyArray<infer A>
     ? ReadonlyArray<HandleThunkActionCreator<A>>
     : 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<RootState> = useSelector;
+ */
+export interface TypedUseSelectorHook<TState> {
+  <TSelected>(
+    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(<ReduxBugDemo />)
 
       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)
       }