diff --git a/index.d.ts b/index.d.ts index c1a16080bf4..1cf5a704337 100644 --- a/index.d.ts +++ b/index.d.ts @@ -90,12 +90,13 @@ export type ReducersMapObject = { * @returns A reducer function that invokes every reducer inside the passed * object, and builds a state object with the same shape. */ -export function combineReducers( - reducers: ReducersMapObject -): Reducer -export function combineReducers( - reducers: ReducersMapObject -): Reducer +export function combineReducers>( + reducers: T +): Reducer, InferActionTypes>> + +type InferActionTypes = R extends Reducer ? A : AnyAction +type InferReducerTypes = T extends Record ? R : Reducer +type InferStateType = T extends ReducersMapObject ? S : never /* store */ diff --git a/test/typescript/reducers.ts b/test/typescript/reducers.ts index 29376714f3f..eb03eaf6d72 100644 --- a/test/typescript/reducers.ts +++ b/test/typescript/reducers.ts @@ -42,7 +42,7 @@ function simple() { // Combined reducer also accepts any action. const combined = combineReducers({ sub: reducer }) - let cs: { sub: State } = combined(undefined, { type: 'init' }) + let cs = combined(undefined, { type: 'init' }) cs = combined(cs, { type: 'INCREMENT', count: 10 }) // Combined reducer's state is strictly checked. @@ -110,17 +110,18 @@ function discriminated() { // typings:expect-error s = reducer(s, { type: 'SOME_OTHER_TYPE', someField: 'value' }) - // Combined reducer accepts any action by default which allows to include - // third-party reducers without the need to add their actions to the union. - const combined = combineReducers({ sub: reducer }) + // Combined reducer accepts a union actions types accepted each reducer, + // which can be very permissive for unknown third-party reducers. + const combined = combineReducers({ + sub: reducer, + unknown: (state => state) as Reducer + }) - let cs: { sub: State } = combined(undefined, { type: 'init' }) - cs = combined(cs, { type: 'SOME_OTHER_TYPE' }) + let cs = combined(undefined, { type: 'init' }) + cs = combined(cs, { type: 'SOME_OTHER_TYPE', someField: 'value' }) // Combined reducer can be made to only accept known actions. - const strictCombined = combineReducers<{ sub: State }, MyAction>({ - sub: reducer - }) + const strictCombined = combineReducers({ sub: reducer }) strictCombined(cs, { type: 'INCREMENT' }) // typings:expect-error @@ -179,7 +180,7 @@ function typeGuards() { const combined = combineReducers({ sub: reducer }) - let cs: { sub: State } = combined(undefined, { type: 'init' }) + let cs = combined(undefined, { type: 'init' }) cs = combined(cs, { type: 'INCREMENT', count: 10 }) }