Skip to content
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

Infer RootState type from RootReducer #75

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 17 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ export const withState = <P extends WrappedComponentProps>(
}

render() {
const { ...remainingProps } = this.props;
const remainingProps = Object.assign({}, this.props);
const { count } = this.state;

return (
Expand Down Expand Up @@ -556,7 +556,7 @@ export const withErrorBoundary = <P extends WrappedComponentProps>(
}

render() {
const { children, ...remainingProps } = this.props;
const { children, ...remainingProps } = this.props as any;
const { error } = this.state;

if (error) {
Expand Down Expand Up @@ -958,29 +958,30 @@ describe('Todos Logic', () => {
### Create Root State and Root Action Types

#### `RootState` - interface representing redux state tree
Can be imported in connected components to provide type-safety to Redux `connect` function
Can be imported in connected components to provide type-safety to Redux `connect` function.

Because the RootState is the sum of it's reducers' return types (plus any store Enhancers we may choose to append), TypeScript can infer its shape entirely from Lookup Types and `ReturnType<>`.

```tsx
import { combineReducers } from 'redux';
import { routerReducer, RouterState } from 'react-router-redux';

import { countersReducer, CountersState } from '@src/redux/counters';
import { todosReducer, TodosState } from '@src/redux/todos';
import { routerReducer } from 'react-router-redux';

interface StoreEnhancerState { }

export interface RootState extends StoreEnhancerState {
router: RouterState;
counters: CountersState;
todos: TodosState;
}
import { countersReducer } from '@src/redux/counters';
import { todosReducer} from '@src/redux/todos';

import { RootAction } from '@src/redux';
export const rootReducer = combineReducers<RootState, RootAction>({

const reducerMap = {
router: routerReducer,
counters: countersReducer,
todos: todosReducer,
});
};

interface StoreEnhancerState { }

export type RootState = { [K in keyof typeof reducerMap]: ReturnType<typeof reducerMap[K]> } & StoreEnhancerState;

export const rootReducer = combineReducers<RootState, RootAction>(reducerMap);

```

Expand Down
4 changes: 3 additions & 1 deletion docs/markdown/2_redux.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,9 @@ state.counterPairs[0].immutableCounter2 = 1; // Error, cannot be mutated
### Create Root State and Root Action Types

#### `RootState` - interface representing redux state tree
Can be imported in connected components to provide type-safety to Redux `connect` function
Can be imported in connected components to provide type-safety to Redux `connect` function.

Because the RootState is the sum of it's reducers' return types (plus any store Enhancers we may choose to append), TypeScript can infer its shape entirely from Lookup Types and `ReturnType<>`.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind to change it to make it easier to understand for TS beginners, something like this (don't need to go into implementation details, a concept should be enough):

  • TypeScript can infer its shape entirely from rootReducer implementation.


::example='../../playground/src/redux/root-reducer.ts'::

Expand Down
2 changes: 1 addition & 1 deletion playground/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
"ts-jest": "22.0.1",
"tslint": "5.8.0",
"tslint-react": "3.3.3",
"typescript": "2.7.2",
"typescript": "2.8.3",
"webpack": "3.10.0",
"webpack-blocks": "1.0.0-rc.2"
}
Expand Down
2 changes: 1 addition & 1 deletion playground/src/hoc/with-error-boundary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const withErrorBoundary = <P extends WrappedComponentProps>(
}

render() {
const { children, ...remainingProps } = this.props;
const { children, ...remainingProps } = this.props as any;
const { error } = this.state;

if (error) {
Expand Down
2 changes: 1 addition & 1 deletion playground/src/hoc/with-state.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const withState = <P extends WrappedComponentProps>(
}

render() {
const { ...remainingProps } = this.props;
const remainingProps = Object.assign({}, this.props);
const { count } = this.state;

return (
Expand Down
25 changes: 12 additions & 13 deletions playground/src/redux/root-reducer.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
import { combineReducers } from 'redux';
import { routerReducer, RouterState } from 'react-router-redux';
import { routerReducer } from 'react-router-redux';

import { countersReducer, CountersState } from '@src/redux/counters';
import { todosReducer, TodosState } from '@src/redux/todos';

interface StoreEnhancerState { }

export interface RootState extends StoreEnhancerState {
router: RouterState;
counters: CountersState;
todos: TodosState;
}
import { countersReducer } from '@src/redux/counters';
import { todosReducer} from '@src/redux/todos';

import { RootAction } from '@src/redux';
export const rootReducer = combineReducers<RootState, RootAction>({

const reducerMap = {
router: routerReducer,
counters: countersReducer,
todos: todosReducer,
});
};

interface StoreEnhancerState { }

export type RootState = { [K in keyof typeof reducerMap]: ReturnType<typeof reducerMap[K]> } & StoreEnhancerState;

export const rootReducer = combineReducers<RootState, RootAction>(reducerMap);
6 changes: 3 additions & 3 deletions playground/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7211,9 +7211,9 @@ [email protected]:
version "1.1.2"
resolved "https://registry.yarnpkg.com/typesafe-actions/-/typesafe-actions-1.1.2.tgz#af88ede3ee254be425c3e0e02de11b182830ae48"

typescript@2.7.2:
version "2.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.7.2.tgz#2d615a1ef4aee4f574425cdff7026edf81919836"
typescript@2.8.3:
version "2.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.8.3.tgz#5d817f9b6f31bb871835f4edf0089f21abe6c170"

typescript@^2.4.2:
version "2.6.2"
Expand Down