Skip to content

Commit 0a3946b

Browse files
committed
refactoring playground for new patterns with improvements possible by TS v2.8+
1 parent e0cff22 commit 0a3946b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+14354
-8630
lines changed

README.md

Lines changed: 68 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ _"This guide is a **living compendium** documenting the most important patterns
44

55
To provide the best experience we focus on the symbiosis of type-safe complementary libraries and learning the concepts like [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html), [Control flow analysis](https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#control-flow-based-type-analysis), [Generics](https://www.typescriptlang.org/docs/handbook/generics.html) and some [Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html).
66

7-
(_Compatible with **TypeScript v2.7.2**_)
8-
97
[![Join the chat at https://gitter.im/react-redux-typescript-guide/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/react-redux-typescript-guide/Lobby)
108

9+
> _Now compatible with **TypeScript v2.8.3**_
10+
1111
> #### _Found it usefull? Want more updates?_ [**Give it a :star2:**](https://github.com/piotrwitek/react-redux-typescript-patterns/stargazers)
1212
1313
### Goals
1414
- Complete type safety (with [`--strict`](https://www.typescriptlang.org/docs/handbook/compiler-options.html) flag) without loosing type information downstream through all the layers of our application (e.g. no type assertions or hacking with `any` type)
1515
- Make type annotations concise by eliminating redudancy in types using advanced TypeScript Language features like **Type Inference** and **Control flow analysis**
1616
- Reduce repetition and complexity of types with TypeScript focused [complementary libraries](#complementary-libraries)
1717

18+
### Complementary Projects
19+
- Typesafe Action Creators for Redux / Flux Architectures [typesafe-actions](https://github.com/piotrwitek/typesafe-actions)
20+
- Reference implementation of Todo-App: [typesafe-actions-todo-app](https://github.com/piotrwitek/typesafe-actions-todo-app)
21+
- Utility Types for TypeScript: [utility-types](https://github.com/piotrwitek/utility-types)
22+
1823
### Playground Project
1924
[![Codeship Status for piotrwitek/react-redux-typescript-guide](https://app.codeship.com/projects/11eb8c10-d117-0135-6c51-26e28af241d2/status?branch=master)](https://app.codeship.com/projects/262359)
2025

@@ -54,7 +59,6 @@ You should check Playground Project located in the `/playground` folder. It is a
5459
- [Default and Named Module Exports](#default-and-named-module-exports)
5560
- [FAQ](#faq)
5661
- [Contribution Guide](#contribution-guide)
57-
- [Project Examples](#project-examples)
5862
- [Tutorials](#tutorials)
5963

6064
---
@@ -711,11 +715,11 @@ import { RootState } from '@src/redux';
711715
import { countersActions, countersSelectors } from '@src/redux/counters';
712716
import { SFCCounter } from '@src/components';
713717

714-
export interface SFCCounterConnectedExtended {
718+
export interface SFCCounterConnectedExtendedProps {
715719
initialCount: number;
716720
}
717721

718-
const mapStateToProps = (state: RootState, ownProps: SFCCounterConnectedExtended) => ({
722+
const mapStateToProps = (state: RootState, ownProps: SFCCounterConnectedExtendedProps) => ({
719723
count: countersSelectors.getReduxCounter(state) + ownProps.initialCount,
720724
});
721725

@@ -731,12 +735,7 @@ import * as React from 'react';
731735

732736
import { SFCCounterConnectedExtended } from '@src/connected';
733737

734-
export default () => (
735-
<SFCCounterConnectedExtended
736-
label={'SFCCounterConnectedExtended'}
737-
initialCount={10}
738-
/>
739-
);
738+
export default () => <SFCCounterConnectedExtended label={'SFCCounterConnectedExtended'} initialCount={10} />;
740739

741740
```
742741
</p></details>
@@ -758,23 +757,24 @@ All that without losing type-safety! Please check this very short [Tutorial](htt
758757
```tsx
759758
import { createAction } from 'typesafe-actions';
760759

761-
export const countersActions = {
762-
increment: createAction('INCREMENT'),
763-
add: createAction('ADD', (amount: number) => ({
764-
type: 'ADD',
765-
payload: amount,
766-
})),
767-
};
760+
export const increment = createAction('counters/INCREMENT');
761+
762+
export const add = createAction('counters/ADD', resolve => {
763+
return (amount: number) => resolve(amount);
764+
});
768765

769766
```
770767
<details><summary>show usage</summary><p>
771768
772769
```tsx
773770
import store from '@src/store';
774-
import { countersActions } from '@src/redux/counters';
771+
import { actions as counters } from '@src/redux/counters';
775772

776773
// store.dispatch(countersActions.increment(1)); // Error: Expected 0 arguments, but got 1.
777-
store.dispatch(countersActions.increment()); // OK => { type: "INCREMENT" }
774+
store.dispatch(counters.increment()); // OK
775+
776+
// store.dispatch(countersActions.add()); // Error: Expected 1 arguments, but got 0.
777+
store.dispatch(counters.add(1)); // OK
778778

779779
```
780780
</p></details>
@@ -845,111 +845,102 @@ state.counterPairs[0].immutableCounter2 = 1; // Error, cannot be mutated
845845
846846
```tsx
847847
import { combineReducers } from 'redux';
848-
import { getType } from 'typesafe-actions';
848+
import { ActionsUnion } from 'typesafe-actions';
849849

850-
import { ITodo, ITodosFilter } from './types';
851-
import { addTodo, toggleTodo, changeFilter } from './actions';
850+
import { Todo, TodosFilter } from './models';
851+
import { ADD, CHANGE_FILTER, TOGGLE } from './types';
852+
import * as actions from './actions';
852853

853854
export type TodosState = {
854855
readonly isFetching: boolean;
855856
readonly errorMessage: string | null;
856-
readonly todos: ITodo[];
857-
readonly todosFilter: ITodosFilter;
857+
readonly todos: Todo[];
858+
readonly todosFilter: TodosFilter;
858859
};
859860

860-
export type RootState = {
861-
readonly todos: TodosState;
862-
};
861+
export type TodosAction = ActionsUnion<typeof actions>;
863862

864-
export const todosReducer = combineReducers<TodosState, TodosAction>({
863+
export default combineReducers<TodosState, TodosAction>({
865864
isFetching: (state = false, action) => {
866865
switch (action.type) {
867-
default: return state;
866+
default:
867+
return state;
868868
}
869869
},
870-
errorMessage: (state = '', action) => {
870+
errorMessage: (state = null, action) => {
871871
switch (action.type) {
872-
default: return state;
872+
default:
873+
return state;
873874
}
874875
},
875876
todos: (state = [], action) => {
876877
switch (action.type) {
877-
case getType(addTodo):
878+
case ADD:
878879
return [...state, action.payload];
879880

880-
case getType(toggleTodo):
881-
return state.map((item) => item.id === action.payload
882-
? { ...item, completed: !item.completed }
883-
: item
884-
);
881+
case TOGGLE:
882+
return state.map(item => (item.id === action.payload ? { ...item, completed: !item.completed } : item));
885883

886-
default: return state;
884+
default:
885+
return state;
887886
}
888887
},
889-
todosFilter: (state = '', action) => {
888+
todosFilter: (state = TodosFilter.All, action) => {
890889
switch (action.type) {
891-
case getType(changeFilter):
890+
case CHANGE_FILTER:
892891
return action.payload;
893892

894-
default: return state;
893+
default:
894+
return state;
895895
}
896896
},
897897
});
898898

899-
// inferring union type of actions
900-
import { $call } from 'utility-types';
901-
import * as actions from './actions';
902-
const returnsOfActions = Object.values(actions).map($call);
903-
export type TodosAction = typeof returnsOfActions[number];
904-
905899
```
906900
907901
[⇧ back to top](#table-of-contents)
908902
909903
### Testing reducer
910904
911905
```tsx
912-
import { todosReducer, TodosState, TodosAction } from './reducer';
913-
import { addTodo, changeFilter, toggleTodo } from './actions';
906+
import { reducer, actions } from './';
914907

915908
/**
916909
* FIXTURES
917910
*/
918911
const activeTodo = { id: '1', completed: false, title: 'active todo' };
919912
const completedTodo = { id: '2', completed: true, title: 'completed todo' };
920913

921-
const initialState = todosReducer(undefined, {});
914+
const initialState = reducer(undefined, {});
922915

923916
/**
924-
* SCENARIOS
917+
* STORIES
925918
*/
926-
describe('Todos Logic', () => {
927-
919+
describe('Todos Stories', () => {
928920
describe('initial state', () => {
929921
it('should match a snapshot', () => {
930922
expect(initialState).toMatchSnapshot();
931923
});
932924
});
933925

934926
describe('adding todos', () => {
935-
it('should add a new todo as the first active element', () => {
936-
const action = addTodo('new todo');
937-
const state = todosReducer(initialState, action);
927+
it('should add a new todo as the first element', () => {
928+
const action = actions.add('new todo');
929+
const state = reducer(initialState, action);
938930
expect(state.todos).toHaveLength(1);
939931
expect(state.todos[0].id).toEqual(action.payload.id);
940932
});
941933
});
942934

943935
describe('toggling completion state', () => {
944-
it('should mark as complete todo with id "1"', () => {
945-
const action = toggleTodo(activeTodo.id);
936+
it('should mark active todo as complete', () => {
937+
const action = actions.toggle(activeTodo.id);
946938
const state0 = { ...initialState, todos: [activeTodo] };
947939
expect(state0.todos[0].completed).toBeFalsy();
948-
const state1 = todosReducer(state0, action);
940+
const state1 = reducer(state0, action);
949941
expect(state1.todos[0].completed).toBeTruthy();
950942
});
951943
});
952-
953944
});
954945

955946
```
@@ -996,26 +987,23 @@ export const rootReducer = combineReducers<RootState, RootAction>({
996987
Can be imported in various layers receiving or sending redux actions like: reducers, sagas or redux-observables epics
997988
998989
```tsx
999-
// RootActions
1000990
import { RouterAction, LocationChangeAction } from 'react-router-redux';
1001-
import { $call } from 'utility-types';
991+
import { ActionsUnion } from 'typesafe-actions';
1002992

1003-
import { countersActions } from '@src/redux/counters';
1004-
import { todosActions } from '@src/redux/todos';
1005-
import { toastsActions } from '@src/redux/toasts';
993+
import { countersActions as counters } from '@src/redux/counters/actions';
994+
import { actions as todos } from '@src/redux/todos';
995+
import { toastsActions as toasts } from '@src/redux/toasts/actions';
1006996

1007-
const returnsOfActions = [
1008-
...Object.values(countersActions),
1009-
...Object.values(todosActions),
1010-
...Object.values(toastsActions),
1011-
].map($call);
997+
export const actions = {
998+
counters,
999+
todos,
1000+
toasts,
1001+
};
10121002

1013-
type AppAction = typeof returnsOfActions[number];
1003+
type AppAction = ActionsUnion<typeof actions>;
10141004
type ReactRouterAction = RouterAction | LocationChangeAction;
10151005

1016-
export type RootAction =
1017-
| AppAction
1018-
| ReactRouterAction;
1006+
export type RootAction = AppAction | ReactRouterAction;
10191007

10201008
```
10211009
@@ -1338,15 +1326,15 @@ configure({ adapter: new Adapter() });
13381326
# Recipes
13391327
13401328
### tsconfig.json
1341-
- Recommended setup for best benefits from type-checking, with support for JSX and ES2016 features
1342-
- Add [`tslib`](https://www.npmjs.com/package/tslib) to minimize bundle size: `npm i tslib` - this will externalize helper functions generated by transpiler and otherwise inlined in your modules
1343-
- Include absolute imports config working with Webpack
1329+
- Recommended baseline config carefully optimized for strict type-checking and optimal webpack workflow
1330+
- Install [`tslib`](https://www.npmjs.com/package/tslib) to cut on bundle size, by using external transpiltion helper module instead of adding them inline: `npm i tslib`
1331+
- Example setup for project relative path imports with Webpack
13441332
13451333
```js
13461334
{
13471335
"compilerOptions": {
1348-
"baseUrl": "./", // enables absolute path imports
1349-
"paths": { // define absolute path mappings
1336+
"baseUrl": "./", // enables project relative paths config
1337+
"paths": { // define paths mappings
13501338
"@src/*": ["src/*"] // will enable -> import { ... } from '@src/components'
13511339
// in webpack you need to add -> resolve: { alias: { '@src': PATH_TO_SRC } }
13521340
},
@@ -1538,12 +1526,6 @@ node ./generator/bin/generate-readme.js
15381526
15391527
---
15401528
1541-
# Project Examples
1542-
1543-
https://github.com/piotrwitek/react-redux-typescript-webpack-starter
1544-
1545-
[⇧ back to top](#table-of-contents)
1546-
15471529
# Tutorials
15481530
> Curated list of relevant in-depth tutorials
15491531

docs/markdown/4_recipes.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
# Recipes
22

33
### tsconfig.json
4-
- Recommended setup for best benefits from type-checking, with support for JSX and ES2016 features
5-
- Add [`tslib`](https://www.npmjs.com/package/tslib) to minimize bundle size: `npm i tslib` - this will externalize helper functions generated by transpiler and otherwise inlined in your modules
6-
- Include absolute imports config working with Webpack
4+
- Recommended baseline config carefully optimized for strict type-checking and optimal webpack workflow
5+
- Install [`tslib`](https://www.npmjs.com/package/tslib) to cut on bundle size, by using external transpiltion helper module instead of adding them inline: `npm i tslib`
6+
- Example setup for project relative path imports with Webpack
77

88
```js
99
{
1010
"compilerOptions": {
11-
"baseUrl": "./", // enables absolute path imports
12-
"paths": { // define absolute path mappings
11+
"baseUrl": "./", // enables project relative paths config
12+
"paths": { // define paths mappings
1313
"@src/*": ["src/*"] // will enable -> import { ... } from '@src/components'
1414
// in webpack you need to add -> resolve: { alias: { '@src': PATH_TO_SRC } }
1515
},

docs/markdown/7_links.md

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
# Project Examples
2-
3-
https://github.com/piotrwitek/react-redux-typescript-webpack-starter
4-
5-
[⇧ back to top](#table-of-contents)
6-
71
# Tutorials
82
> Curated list of relevant in-depth tutorials
93

docs/markdown/_intro.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,22 @@ _"This guide is a **living compendium** documenting the most important patterns
44

55
To provide the best experience we focus on the symbiosis of type-safe complementary libraries and learning the concepts like [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html), [Control flow analysis](https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#control-flow-based-type-analysis), [Generics](https://www.typescriptlang.org/docs/handbook/generics.html) and some [Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html).
66

7-
(_Compatible with **TypeScript v2.7.2**_)
8-
97
[![Join the chat at https://gitter.im/react-redux-typescript-guide/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/react-redux-typescript-guide/Lobby)
108

9+
> _Now compatible with **TypeScript v2.8.3**_
10+
1111
> #### _Found it usefull? Want more updates?_ [**Give it a :star2:**](https://github.com/piotrwitek/react-redux-typescript-patterns/stargazers)
1212
1313
### Goals
1414
- Complete type safety (with [`--strict`](https://www.typescriptlang.org/docs/handbook/compiler-options.html) flag) without loosing type information downstream through all the layers of our application (e.g. no type assertions or hacking with `any` type)
1515
- Make type annotations concise by eliminating redudancy in types using advanced TypeScript Language features like **Type Inference** and **Control flow analysis**
1616
- Reduce repetition and complexity of types with TypeScript focused [complementary libraries](#complementary-libraries)
1717

18+
### Complementary Projects
19+
- Typesafe Action Creators for Redux / Flux Architectures [typesafe-actions](https://github.com/piotrwitek/typesafe-actions)
20+
- Reference implementation of Todo-App: [typesafe-actions-todo-app](https://github.com/piotrwitek/typesafe-actions-todo-app)
21+
- Utility Types for TypeScript: [utility-types](https://github.com/piotrwitek/utility-types)
22+
1823
### Playground Project
1924
[![Codeship Status for piotrwitek/react-redux-typescript-guide](https://app.codeship.com/projects/11eb8c10-d117-0135-6c51-26e28af241d2/status?branch=master)](https://app.codeship.com/projects/262359)
2025

docs/markdown/_toc.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,5 +29,4 @@
2929
- [Default and Named Module Exports](#default-and-named-module-exports)
3030
- [FAQ](#faq)
3131
- [Contribution Guide](#contribution-guide)
32-
- [Project Examples](#project-examples)
3332
- [Tutorials](#tutorials)

playground/.nvmrc

Lines changed: 0 additions & 1 deletion
This file was deleted.

playground/.prettierrc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"printWidth": 80,
3+
"semi": true,
4+
"singleQuote": true,
5+
"trailingComma": "es5"
6+
}

playground/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
This folder is a playground for testing examples from `react-redux-typescript-guide`.
22

33
I have a secondary goal to create a separate project starter that could be used to bootstrap a real world application like this one: https://github.com/gothinkster/realworld
4+
5+
Standard Approach
6+
Opinionated Approach

0 commit comments

Comments
 (0)