diff --git a/src/applyMiddleware.js b/src/applyMiddleware.ts
similarity index 72%
rename from src/applyMiddleware.js
rename to src/applyMiddleware.ts
index 3bfc647b45..3837181241 100644
--- a/src/applyMiddleware.js
+++ b/src/applyMiddleware.ts
@@ -1,4 +1,11 @@
import compose from './compose'
+import {
+ Middleware,
+ StoreEnhancer,
+ StoreCreator,
+ Dispatch,
+ AnyAction
+} from '..'
/**
* Creates a store enhancer that applies middleware to the dispatch method
@@ -16,10 +23,12 @@ import compose from './compose'
* @param {...Function} middlewares The middleware chain to be applied.
* @returns {Function} A store enhancer applying the middleware.
*/
-export default function applyMiddleware(...middlewares) {
- return createStore => (...args) => {
- const store = createStore(...args)
- let dispatch = () => {
+export default function applyMiddleware(
+ ...middlewares: Middleware[]
+): StoreEnhancer {
+ return (createStore: StoreCreator) => (reducer: any, ...args) => {
+ const store = createStore(reducer, ...args)
+ let dispatch: Dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
@@ -28,7 +37,8 @@ export default function applyMiddleware(...middlewares) {
const middlewareAPI = {
getState: store.getState,
- dispatch: (...args) => dispatch(...args)
+ dispatch: (action: AnyAction, ...args: any[]) =>
+ (dispatch as any)(action, ...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI))
dispatch = compose(...chain)(store.dispatch)
diff --git a/src/bindActionCreators.js b/src/bindActionCreators.ts
similarity index 67%
rename from src/bindActionCreators.js
rename to src/bindActionCreators.ts
index 0e47477def..bcef3477a5 100644
--- a/src/bindActionCreators.js
+++ b/src/bindActionCreators.ts
@@ -1,6 +1,11 @@
-function bindActionCreator(actionCreator, dispatch) {
- return function() {
- return dispatch(actionCreator.apply(this, arguments))
+import { AnyAction, ActionCreator, Dispatch, ActionCreatorsMapObject } from '..'
+
+function bindActionCreator(
+ actionCreator: ActionCreator,
+ dispatch: Dispatch
+) {
+ return function(this: any) {
+ return dispatch(actionCreator.apply(this, (arguments as unknown) as any[]))
}
}
@@ -13,19 +18,22 @@ function bindActionCreator(actionCreator, dispatch) {
* For convenience, you can also pass an action creator as the first argument,
* and get a dispatch wrapped function in return.
*
- * @param {Function|Object} actionCreators An object whose values are action
+ * @param actionCreators An object whose values are action
* creator functions. One handy way to obtain it is to use ES6 `import * as`
* syntax. You may also pass a single function.
*
- * @param {Function} dispatch The `dispatch` function available on your Redux
+ * @param dispatch The `dispatch` function available on your Redux
* store.
*
- * @returns {Function|Object} The object mimicking the original object, but with
+ * @returns The object mimicking the original object, but with
* every action creator wrapped into the `dispatch` call. If you passed a
* function as `actionCreators`, the return value will also be a single
* function.
*/
-export default function bindActionCreators(actionCreators, dispatch) {
+export default function bindActionCreators(
+ actionCreators: ActionCreator | ActionCreatorsMapObject,
+ dispatch: Dispatch
+) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
@@ -39,7 +47,7 @@ export default function bindActionCreators(actionCreators, dispatch) {
)
}
- const boundActionCreators = {}
+ const boundActionCreators: ActionCreatorsMapObject = {}
for (const key in actionCreators) {
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
diff --git a/src/combineReducers.js b/src/combineReducers.ts
similarity index 89%
rename from src/combineReducers.js
rename to src/combineReducers.ts
index bdbcfb2291..488eff3cc6 100644
--- a/src/combineReducers.js
+++ b/src/combineReducers.ts
@@ -1,8 +1,9 @@
+import { AnyAction, Action, ReducersMapObject } from '..'
import ActionTypes from './utils/actionTypes'
import warning from './utils/warning'
import isPlainObject from './utils/isPlainObject'
-function getUndefinedStateErrorMessage(key, action) {
+function getUndefinedStateErrorMessage(key: string, action: Action) {
const actionType = action && action.type
const actionDescription =
(actionType && `action "${String(actionType)}"`) || 'an action'
@@ -15,10 +16,10 @@ function getUndefinedStateErrorMessage(key, action) {
}
function getUnexpectedStateShapeWarningMessage(
- inputState,
- reducers,
- action,
- unexpectedKeyCache
+ inputState: object,
+ reducers: ReducersMapObject,
+ action: Action,
+ unexpectedKeyCache: { [key: string]: true }
) {
const reducerKeys = Object.keys(reducers)
const argumentName =
@@ -36,7 +37,7 @@ function getUnexpectedStateShapeWarningMessage(
if (!isPlainObject(inputState)) {
return (
`The ${argumentName} has unexpected type of "` +
- {}.toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
+ ({} as any).toString.call(inputState).match(/\s([a-z|A-Z]+)/)[1] +
`". Expected argument to be an object with the following ` +
`keys: "${reducerKeys.join('", "')}"`
)
@@ -62,7 +63,7 @@ function getUnexpectedStateShapeWarningMessage(
}
}
-function assertReducerShape(reducers) {
+function assertReducerShape(reducers: ReducersMapObject) {
Object.keys(reducers).forEach(key => {
const reducer = reducers[key]
const initialState = reducer(undefined, { type: ActionTypes.INIT })
@@ -110,9 +111,9 @@ function assertReducerShape(reducers) {
* @returns {Function} A reducer function that invokes every reducer inside the
* passed object, and builds a state object with the same shape.
*/
-export default function combineReducers(reducers) {
+export default function combineReducers(reducers: ReducersMapObject) {
const reducerKeys = Object.keys(reducers)
- const finalReducers = {}
+ const finalReducers: ReducersMapObject = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
@@ -130,19 +131,19 @@ export default function combineReducers(reducers) {
// This is used to make sure we don't warn about the same
// keys multiple times.
- let unexpectedKeyCache
+ let unexpectedKeyCache: { [key: string]: true }
if (process.env.NODE_ENV !== 'production') {
unexpectedKeyCache = {}
}
- let shapeAssertionError
+ let shapeAssertionError: Error
try {
assertReducerShape(finalReducers)
} catch (e) {
shapeAssertionError = e
}
- return function combination(state = {}, action) {
+ return function combination(state: any = {}, action: AnyAction) {
if (shapeAssertionError) {
throw shapeAssertionError
}
@@ -160,7 +161,7 @@ export default function combineReducers(reducers) {
}
let hasChanged = false
- const nextState = {}
+ const nextState: any = {}
for (let i = 0; i < finalReducerKeys.length; i++) {
const key = finalReducerKeys[i]
const reducer = finalReducers[key]
diff --git a/src/compose.js b/src/compose.ts
similarity index 77%
rename from src/compose.js
rename to src/compose.ts
index be6cc34ef1..a87beec0d4 100644
--- a/src/compose.js
+++ b/src/compose.ts
@@ -9,14 +9,14 @@
* (...args) => f(g(h(...args))).
*/
-export default function compose(...funcs) {
+export default function compose(...funcs: Function[]) {
if (funcs.length === 0) {
- return arg => arg
+ return (arg: any) => arg
}
if (funcs.length === 1) {
return funcs[0]
}
- return funcs.reduce((a, b) => (...args) => a(b(...args)))
+ return funcs.reduce((a, b) => (...args: any) => a(b(...args)))
}
diff --git a/src/createStore.js b/src/createStore.ts
similarity index 84%
rename from src/createStore.js
rename to src/createStore.ts
index 57c57d7636..76ccc430ec 100644
--- a/src/createStore.js
+++ b/src/createStore.ts
@@ -1,5 +1,14 @@
import $$observable from 'symbol-observable'
+import {
+ Store,
+ Action,
+ Reducer,
+ PreloadedState,
+ StoreEnhancer,
+ Dispatch,
+ Observer
+} from '..'
import ActionTypes from './utils/actionTypes'
import isPlainObject from './utils/isPlainObject'
@@ -28,7 +37,20 @@ import isPlainObject from './utils/isPlainObject'
* @returns {Store} A Redux store that lets you read the state, dispatch actions
* and subscribe to changes.
*/
-export default function createStore(reducer, preloadedState, enhancer) {
+export default function createStore(
+ reducer: Reducer,
+ enhancer?: StoreEnhancer
+): Store & Ext
+export default function createStore(
+ reducer: Reducer,
+ preloadedState?: PreloadedState,
+ enhancer?: StoreEnhancer
+): Store & Ext
+export default function createStore(
+ reducer: Reducer,
+ preloadedState?: PreloadedState | StoreEnhancer,
+ enhancer?: StoreEnhancer
+) {
if (
(typeof preloadedState === 'function' && typeof enhancer === 'function') ||
(typeof enhancer === 'function' && typeof arguments[3] === 'function')
@@ -41,7 +63,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
}
if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') {
- enhancer = preloadedState
+ enhancer = preloadedState as StoreEnhancer
preloadedState = undefined
}
@@ -50,7 +72,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
throw new Error('Expected the enhancer to be a function.')
}
- return enhancer(createStore)(reducer, preloadedState)
+ return enhancer(createStore)(reducer, preloadedState as PreloadedState)
}
if (typeof reducer !== 'function') {
@@ -59,7 +81,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
let currentReducer = reducer
let currentState = preloadedState
- let currentListeners = []
+ let currentListeners: (() => void)[] | null = []
let nextListeners = currentListeners
let isDispatching = false
@@ -81,7 +103,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
*
* @returns {any} The current state tree of your application.
*/
- function getState() {
+ function getState(): S {
if (isDispatching) {
throw new Error(
'You may not call store.getState() while the reducer is executing. ' +
@@ -90,7 +112,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
)
}
- return currentState
+ return currentState as S
}
/**
@@ -116,7 +138,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
* @param {Function} listener A callback to be invoked on every dispatch.
* @returns {Function} A function to remove this change listener.
*/
- function subscribe(listener) {
+ function subscribe(listener: () => void) {
if (typeof listener !== 'function') {
throw new Error('Expected the listener to be a function.')
}
@@ -181,7 +203,7 @@ export default function createStore(reducer, preloadedState, enhancer) {
* Note that, if you use a custom middleware, it may wrap `dispatch()` to
* return something else (for example, a Promise you can await).
*/
- function dispatch(action) {
+ function dispatch(action: A) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
@@ -202,7 +224,10 @@ export default function createStore(reducer, preloadedState, enhancer) {
try {
isDispatching = true
- currentState = currentReducer(currentState, action)
+ ;(currentState as S) = currentReducer(
+ currentState as (S | undefined),
+ action
+ )
} finally {
isDispatching = false
}
@@ -226,19 +251,25 @@ export default function createStore(reducer, preloadedState, enhancer) {
* @param {Function} nextReducer The reducer for the store to use instead.
* @returns {Store} The same store instance with a new reducer in place.
*/
- function replaceReducer(nextReducer) {
+ function replaceReducer(
+ nextReducer: Reducer
+ ): Store {
if (typeof nextReducer !== 'function') {
throw new Error('Expected the nextReducer to be a function.')
}
- currentReducer = nextReducer
+ ;((currentReducer as unknown) as Reducer<
+ NewState,
+ NewActions
+ >) = nextReducer
// This action has a similiar effect to ActionTypes.INIT.
// Any reducers that existed in both the new and old rootReducer
// will receive the previous state. This effectively populates
// the new state tree with any relevant data from the old one.
- dispatch({ type: ActionTypes.REPLACE })
- return store
+ const newStore = (store as unknown) as Store
+ dispatch({ type: ActionTypes.REPLACE } as A)
+ return newStore
}
/**
@@ -258,14 +289,15 @@ export default function createStore(reducer, preloadedState, enhancer) {
* be used to unsubscribe the observable from the store, and prevent further
* emission of values from the observable.
*/
- subscribe(observer) {
+ subscribe(observer: unknown) {
if (typeof observer !== 'object' || observer === null) {
throw new TypeError('Expected the observer to be an object.')
}
function observeState() {
- if (observer.next) {
- observer.next(getState())
+ const observerAsObserver = observer as Observer
+ if (observerAsObserver.next) {
+ observerAsObserver.next(getState())
}
}
@@ -283,14 +315,14 @@ export default function createStore(reducer, preloadedState, enhancer) {
// When a store is created, an "INIT" action is dispatched so that every
// reducer returns their initial state. This effectively populates
// the initial state tree.
- dispatch({ type: ActionTypes.INIT })
+ dispatch({ type: ActionTypes.INIT } as A)
- const store = {
- dispatch,
+ const store: Store = ({
+ dispatch: dispatch as Dispatch,
subscribe,
getState,
replaceReducer,
[$$observable]: observable
- }
+ } as unknown) as Store
return store
}
diff --git a/src/index.ts b/src/index.ts
index efedbc4fed..d3f1161ecc 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,9 +1,31 @@
+import {
+ Dispatch,
+ Unsubscribe,
+ Observable,
+ Observer,
+ Store,
+ DeepPartial,
+ StoreCreator,
+ StoreEnhancer
+} from '..'
import createStore from './createStore'
+import {
+ CombinedState,
+ PreloadedState,
+ Reducer,
+ ReducerFromReducersMapObject,
+ StateFromReducersMapObject,
+ ActionFromReducer,
+ ActionFromReducersMapObject
+} from '..'
import combineReducers from './combineReducers'
+import { ActionCreator, ActionCreatorsMapObject } from '..'
import bindActionCreators from './bindActionCreators'
+import { MiddlewareAPI, Middleware } from '..'
import applyMiddleware from './applyMiddleware'
import compose from './compose'
import warning from './utils/warning'
+import { Action, AnyAction } from '..'
import __DO_NOT_USE__ActionTypes from './utils/actionTypes'
/*
@@ -27,6 +49,34 @@ if (
}
export {
+ // types
+ // actions
+ Action,
+ AnyAction,
+ // action creators
+ ActionCreator,
+ ActionCreatorsMapObject,
+ // reducers
+ CombinedState,
+ PreloadedState,
+ Reducer,
+ ReducerFromReducersMapObject,
+ StateFromReducersMapObject,
+ ActionFromReducer,
+ ActionFromReducersMapObject,
+ // middleware
+ MiddlewareAPI,
+ Middleware,
+ // store
+ Dispatch,
+ Unsubscribe,
+ Observable,
+ Observer,
+ Store,
+ DeepPartial,
+ StoreCreator,
+ StoreEnhancer,
+ // things
createStore,
combineReducers,
bindActionCreators,
diff --git a/src/utils/actionTypes.js b/src/utils/actionTypes.ts
similarity index 100%
rename from src/utils/actionTypes.js
rename to src/utils/actionTypes.ts
diff --git a/src/utils/isPlainObject.js b/src/utils/isPlainObject.ts
similarity index 86%
rename from src/utils/isPlainObject.js
rename to src/utils/isPlainObject.ts
index 15ce724283..4281d65d24 100644
--- a/src/utils/isPlainObject.js
+++ b/src/utils/isPlainObject.ts
@@ -2,7 +2,7 @@
* @param {any} obj The object to inspect.
* @returns {boolean} True if the argument appears to be a plain object.
*/
-export default function isPlainObject(obj) {
+export default function isPlainObject(obj: unknown) {
if (typeof obj !== 'object' || obj === null) return false
let proto = obj
diff --git a/src/utils/warning.js b/src/utils/warning.ts
similarity index 91%
rename from src/utils/warning.js
rename to src/utils/warning.ts
index d35e99b726..e0ea312f5d 100644
--- a/src/utils/warning.js
+++ b/src/utils/warning.ts
@@ -4,7 +4,7 @@
* @param {String} message The warning message.
* @returns {void}
*/
-export default function warning(message) {
+export default function warning(message: string) {
/* eslint-disable no-console */
if (typeof console !== 'undefined' && typeof console.error === 'function') {
console.error(message)
diff --git a/test/applyMiddleware.spec.ts b/test/applyMiddleware.spec.ts
index 69a4e5766b..fefd849b15 100644
--- a/test/applyMiddleware.spec.ts
+++ b/test/applyMiddleware.spec.ts
@@ -1,11 +1,4 @@
-import {
- createStore,
- applyMiddleware,
- Middleware,
- Dispatch,
- AnyAction,
- Action
-} from '..'
+import { createStore, applyMiddleware, Middleware, AnyAction, Action } from '..'
import * as reducers from './helpers/reducers'
import { addTodo, addTodoAsync, addTodoIfEmpty } from './helpers/actionCreators'
import { thunk } from './helpers/middleware'
diff --git a/test/typescript/tsconfig.json b/test/typescript/tsconfig.json
index be426fdcac..497fa459db 100644
--- a/test/typescript/tsconfig.json
+++ b/test/typescript/tsconfig.json
@@ -1,6 +1,6 @@
{
"compilerOptions": {
- "lib": ["es2015", "dom"],
+ "lib": ["es2017", "dom"],
"strict": true,
"baseUrl": "../..",
"paths": {
diff --git a/tsconfig.json b/tsconfig.json
index 052d09d209..0abee9b5c3 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -7,11 +7,11 @@
"dom",
"es2017"
] /* Specify library files to be included in the compilation. */,
- "allowJs": true /* Allow javascript files to be compiled. */,
+ // "allowJs": true /* Allow javascript files to be compiled. */,
// "checkJs": true, /* Report errors in .js files. */
"jsx": "react" /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */,
- // "declaration": true, /* Generates corresponding '.d.ts' file. */
- // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
+ // "declaration": true /* Generates corresponding '.d.ts' file. */,
+ // "declarationMap": true /* Generates a sourcemap for each corresponding '.d.ts' file. */,
"sourceMap": true /* Generates corresponding '.map' file. */,
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "." /* Redirect output structure to the directory. */,
@@ -21,7 +21,7 @@
"noEmit": true /* Do not emit outputs. */,
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
- "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
+ // "isolatedModules": true /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */,
/* Strict Type-Checking Options */
"strict": true /* Enable all strict type-checking options. */,