├── .gitignore ├── README.md ├── index.tsx └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Actually typing your React-Redux program 2 | 3 | A guide for those who are stuck not being able to migrate their apps away from JS/TypeScript/Flow. See [index.ts](./index.ts) 4 | 5 | Seriously though, just use anything else. -------------------------------------------------------------------------------- /index.tsx: -------------------------------------------------------------------------------- 1 | // install @types/react-redux and other nonsense 2 | 3 | import * as React from "react"; 4 | import { connect } from "react-redux"; 5 | 6 | // import { YourActualAppState, YourActualAppDispatch } from './wherever-it-is.ts' 7 | type YourActualAppState = { 8 | whatever: { 9 | apple: String; 10 | banana: String; 11 | }; 12 | }; 13 | // hopefully you make a union of your actual actions in YourActualAppActions or something 14 | type YourActualAppDispatch = (action: { type: "ASDF" }) => void; 15 | 16 | // map state to props function that returns { whateverStateField: string } 17 | export function mapStateToProps({ whatever }: YourActualAppState) { 18 | return { 19 | whateverStateField: "whatever" 20 | }; 21 | } 22 | 23 | // map dispatch to props function that returns { whateverDispatchField: (...) => void } 24 | export function mapDispatchToProps(dispatch: YourActualAppDispatch) { 25 | return { 26 | whateverDispatchField: (whatever: { kiwi: string }) => 27 | dispatch({ type: "ASDF" }) 28 | }; 29 | } 30 | 31 | // function for making a "value" of the result type 32 | function makeExtractable(fn: (input: Input) => Output): Output { 33 | return (false as true) && fn({} as any); // trick compiler into evaluating return value, maybe this will be a feature in 4.0+++ 34 | } 35 | 36 | // use makeExtractable on mapStateToProps 37 | const extractStateProps = makeExtractable(mapStateToProps); 38 | type StateProps = typeof extractStateProps; 39 | // on hover: 40 | // type StateProps = { 41 | // whateverStateField: string; 42 | // } 43 | 44 | // same with extractDispatchProps 45 | const extractDispatchProps = makeExtractable(mapDispatchToProps); 46 | type DispatchProps = typeof extractDispatchProps; 47 | // on hover: 48 | // type DispatchProps = { 49 | // whateverDispatchField: (whatever: { kiwi: string; }) => void; 50 | // } 51 | 52 | // whatever props this actually exposes 53 | export type Props = { 54 | whateverPropField: { 55 | pineapple: string; 56 | }; 57 | }; 58 | 59 | // Clean/Pick trick suggested by @gcanti for cleaner intersections 60 | type Clean = Pick; 61 | // intersection type for your component 62 | type Type = Clean; 63 | // so now: 64 | // type Type = { 65 | // whateverPropField: { 66 | // pineapple: string; 67 | // }; 68 | // whateverStateField: string; 69 | // whateverDispatchField: (whatever: { 70 | // kiwi: string; 71 | // }) => void;| 72 | // } 73 | 74 | // OR 75 | // type Type = Props & StateProps & DispatchProps // intersection type for your component 76 | // type Type = Props & { 77 | // whateverStateField: string; 78 | // } & { 79 | // whateverDispatchField: (whatever: { 80 | // kiwi: string; 81 | // }) => void; 82 | // } 83 | 84 | // important: set Props for exporting the type 85 | export const YourComponent: React.ComponentClass = connect( 86 | mapStateToProps, 87 | mapDispatchToProps 88 | )(function(props: Type) { // Type used here for the combined props from connect 89 | return
90 | }); 91 | 92 | // congrats, now you have something actually useful that type checks in your whole app, unlike most every example I've seen 93 | // remember: if you value your sanity, choose any compile-to-JS language that isn't Javascript/Typescript/Flow 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bullshit", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@types/react": "^16.0.23", 13 | "@types/react-redux": "^5.0.12", 14 | "@types/redux": "^3.6.0", 15 | "prettier": "^1.8.2", 16 | "react": "^16.1.1", 17 | "react-redux": "^5.0.6", 18 | "typescript": "^2.6.1" 19 | } 20 | } 21 | --------------------------------------------------------------------------------