-
-
Notifications
You must be signed in to change notification settings - Fork 15.2k
Improve TypeScript types #3566
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Improve TypeScript types #3566
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
import compose from './compose' | ||
import { Middleware, MiddlewareAPI } from './types/middleware' | ||
import { AnyAction } from './types/actions' | ||
import { StoreEnhancer, StoreCreator, Dispatch } from './types/store' | ||
import { Reducer } from './types/reducers' | ||
import { | ||
Dispatch, | ||
StoreEnhancer, | ||
StoreEnhancerStoreCreator | ||
} from './types/store' | ||
|
||
/** | ||
* Creates a store enhancer that applies middleware to the dispatch method | ||
|
@@ -23,40 +25,40 @@ import { Reducer } from './types/reducers' | |
* @template Ext Dispatch signature added by a middleware. | ||
* @template S The type of the state supported by a middleware. | ||
*/ | ||
export default function applyMiddleware(): StoreEnhancer | ||
export default function applyMiddleware(): StoreEnhancer<{ dispatch: {} }, {}> | ||
export default function applyMiddleware<Ext1, S>( | ||
middleware1: Middleware<Ext1, S, any> | ||
): StoreEnhancer<{ dispatch: Ext1 }> | ||
): StoreEnhancer<{ dispatch: Ext1 }, S> | ||
export default function applyMiddleware<Ext1, Ext2, S>( | ||
middleware1: Middleware<Ext1, S, any>, | ||
middleware2: Middleware<Ext2, S, any> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 }> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 }, S> | ||
export default function applyMiddleware<Ext1, Ext2, Ext3, S>( | ||
middleware1: Middleware<Ext1, S, any>, | ||
middleware2: Middleware<Ext2, S, any>, | ||
middleware3: Middleware<Ext3, S, any> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 }> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 }, S> | ||
export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, S>( | ||
middleware1: Middleware<Ext1, S, any>, | ||
middleware2: Middleware<Ext2, S, any>, | ||
middleware3: Middleware<Ext3, S, any>, | ||
middleware4: Middleware<Ext4, S, any> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 }> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 }, S> | ||
export default function applyMiddleware<Ext1, Ext2, Ext3, Ext4, Ext5, S>( | ||
middleware1: Middleware<Ext1, S, any>, | ||
middleware2: Middleware<Ext2, S, any>, | ||
middleware3: Middleware<Ext3, S, any>, | ||
middleware4: Middleware<Ext4, S, any>, | ||
middleware5: Middleware<Ext5, S, any> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 & Ext5 }> | ||
): StoreEnhancer<{ dispatch: Ext1 & Ext2 & Ext3 & Ext4 & Ext5 }, S> | ||
export default function applyMiddleware<Ext, S = any>( | ||
...middlewares: Middleware<any, S, any>[] | ||
): StoreEnhancer<{ dispatch: Ext }> | ||
export default function applyMiddleware( | ||
): StoreEnhancer<{ dispatch: Ext }, S> | ||
export default function applyMiddleware<S>( | ||
...middlewares: Middleware[] | ||
): StoreEnhancer<any> { | ||
return (createStore: StoreCreator) => <S, A extends AnyAction>( | ||
reducer: Reducer<S, A>, | ||
): StoreEnhancer<any, S> { | ||
return (createStore: StoreEnhancerStoreCreator<any>) => ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not groking why the StoreEnhancer's |
||
reducer, | ||
...args: any[] | ||
) => { | ||
const store = createStore(reducer, ...args) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,7 +97,7 @@ export default function createStore< | |
8000 throw new Error('Expected the reducer to be a function.') | ||
} | ||
|
||
let currentReducer = reducer | ||
let currentReducer: Reducer<any, any> = reducer | ||
let currentState = preloadedState as S | ||
let currentListeners: (() => void)[] | null = [] | ||
let nextListeners = currentListeners | ||
|
@@ -273,11 +273,7 @@ export default function createStore< | |
throw new Error('Expected the nextReducer to be a function.') | ||
} | ||
|
||
// TODO: do this more elegantly | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The elegance is done by line 100, defining a |
||
;((currentReducer as unknown) as Reducer< | ||
NewState, | ||
NewActions | ||
>) = nextReducer | ||
currentReducer = nextReducer | ||
|
||
// This action has a similiar effect to ActionTypes.INIT. | ||
// Any reducers that existed in both the new and old rootReducer | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,19 @@ | ||
import { createStore, applyMiddleware, Middleware, AnyAction, Action } from '..' | ||
import { | ||
createStore, | ||
applyMiddleware, | ||
Middleware, | ||
AnyAction, | ||
Action, | ||
Store | ||
} from '../src' | ||
import { rootActions } from './helpers/actionTypes' | ||
import * as reducers from './helpers/reducers' | ||
import { addTodo, addTodoAsync, addTodoIfEmpty } from './helpers/actionCreators' | ||
import { thunk } from './helpers/middleware' | ||
|
||
describe('applyMiddleware', () => { | ||
it('warns when dispatching during middleware setup', () => { | ||
function dispatchingMiddleware(store) { | ||
function dispatchingMiddleware(store: Store<any, rootActions>) { | ||
store.dispatch(addTodo('Dont dispatch in middleware setup')) | ||
return next => action => next(action) | ||
} | ||
|
@@ -40,8 +48,8 @@ describe('applyMiddleware', () => { | |
]) | ||
}) | ||
|
||
it('passes recursive dispatches through the middleware chain', () => { | ||
function test(spyOnMethods) { | ||
it('passes recursive dispatches through the middleware chain', async () => { | ||
function test(spyOnMethods: jest.Mock) { | ||
return () => next => action => { | ||
spyOnMethods(action) | ||
return next(action) | ||
|
@@ -51,16 +59,11 @@ describe('applyMiddleware', () => { | |
const spy = jest.fn() | ||
const store = applyMiddleware(test(spy), thunk)(createStore)(reducers.todos) | ||
|
||
// the typing for redux-thunk is super complex, so we will use an as unknown hack | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Now that the |
||
const dispatchedValue = (store.dispatch( | ||
addTodoAsync('Use Redux') | ||
) as unknown) as Promise<void> | ||
return dispatchedValue.then(() => { | ||
expect(spy.mock.calls.length).toEqual(2) | ||
}) | ||
await store.dispatch(addTodoAsync('Use Redux')) | ||
expect(spy.mock.calls.length).toEqual(2) | ||
}) | ||
|
||
it('works with thunk middleware', done => { | ||
it('works with thunk middleware', async () => { | ||
const store = applyMiddleware(thunk)(createStore)(reducers.todos) | ||
|
||
store.dispatch(addTodoIfEmpty('Hello')) | ||
|
@@ -91,27 +94,21 @@ describe('applyMiddleware', () => { | |
} | ||
]) | ||
|
||
// the typing for redux-thunk is super complex, so we will use an "as unknown" hack | ||
const dispatchedValue = (store.dispatch( | ||
addTodoAsync('Maybe') | ||
) as unknown) as Promise<void> | ||
dispatchedValue.then(() => { | ||
expect(store.getState()).toEqual([ | ||
{ | ||
id: 1, | ||
text: 'Hello' | ||
}, | ||
{ | ||
id: 2, | ||
text: 'World' | ||
}, | ||
{ | ||
id: 3, | ||
text: 'Maybe' | ||
} | ||
]) | ||
done() | ||
}) | ||
await store.dispatch(addTodoAsync('Maybe')) | ||
expect(store.getState()).toEqual([ | ||
{ | ||
id: 1, | ||
text: 'Hello' | ||
}, | ||
{ | ||
id: 2, | ||
text: 'World' | ||
}, | ||
{ | ||
id: 3, | ||
text: 'Maybe' | ||
} | ||
]) | ||
}) | ||
|
||
it('passes through all arguments of dispatch calls from within middleware', () => { | ||
|
@@ -144,7 +141,7 @@ describe('applyMiddleware', () => { | |
applyMiddleware(multiArgMiddleware, dummyMiddleware) | ||
) | ||
|
||
store.dispatch(spy) | ||
store.dispatch(spy as any) | ||
timdorr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
expect(spy.mock.calls[0]).toEqual(testCallArgs) | ||
}) | ||
}) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,42 @@ | ||
import { Action } from '../..' | ||
|
||
export const ADD_TODO = 'ADD_TODO' | ||
export interface AddTodo extends Action<typeof ADD_TODO> { | ||
text: string | ||
} | ||
|
||
export const DISPATCH_IN_MIDDLE = 'DISPATCH_IN_MIDDLE' | ||
export interface DispatchInMiddle extends Action<typeof DISPATCH_IN_MIDDLE> { | ||
boundDispatchFn: () => void | ||
} | ||
|
||
export const GET_STATE_IN_MIDDLE = 'GET_STATE_IN_MIDDLE' | ||
export interface GetStateInMiddle extends Action<typeof GET_STATE_IN_MIDDLE> { | ||
boundGetStateFn: () => void | ||
} | ||
|
||
export const SUBSCRIBE_IN_MIDDLE = 'SUBSCRIBE_IN_MIDDLE' | ||
export interface SubscribeInMiddle extends Action<typeof SUBSCRIBE_IN_MIDDLE> { | ||
boundSubscribeFn: () => void | ||
} | ||
|
||
export const UNSUBSCRIBE_IN_MIDDLE = 'UNSUBSCRIBE_IN_MIDDLE' | ||
export interface UnsubscribeInMiddle | ||
extends Action<typeof UNSUBSCRIBE_IN_MIDDLE> { | ||
boundUnsubscribeFn: () => void | ||
} | ||
|
||
export const THROW_ERROR = 'THROW_ERROR' | ||
export type ThrowError = Action<typeof THROW_ERROR> | ||
|
||
export const UNKNOWN_ACTION = 'UNKNOWN_ACTION' | ||
export type UnknownAction = Action<typeof UNKNOWN_ACTION> | ||
|
||
export type rootActions = | ||
| AddTodo | ||
| DispatchInMiddle | ||
| GetStateInMiddle | ||
| SubscribeInMiddle | ||
| UnsubscribeInMiddle | ||
| ThrowError | ||
| UnknownAction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we're going to be testing thunks, it only makes sense that this dev dependency would aid in that effort.