├── .all-contributorsrc ├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── advanced-cheatsheet.md │ ├── basic-cheatsheet.md │ ├── general-react-ts-question.md │ ├── hoc-cheatsheet.md │ └── migrating-cheatsheet.md ├── pull_request_template.md ├── stale.yml └── workflows │ ├── main.yml │ ├── mlc_config.json │ └── readme.js.yml ├── .gitignore ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── LICENSE ├── README.md ├── copyFile.js ├── docs ├── advanced │ ├── index.md │ ├── misc-concerns.md │ ├── patterns_by_usecase.md │ ├── patterns_by_version.md │ ├── types-react-ap.md │ └── utility-types.md ├── basic │ ├── editor-integration.md │ ├── examples.md │ ├── getting-started │ │ ├── basic-type-examples.md │ │ ├── class-components.md │ │ ├── concurrent.md │ │ ├── context.md │ │ ├── default-props.md │ │ ├── error-boundaries.md │ │ ├── forms-and-events.md │ │ ├── forward-create-ref.md │ │ ├── function-components.md │ │ ├── hooks.md │ │ └── portals.md │ ├── linting.md │ ├── recommended │ │ ├── codebases.md │ │ ├── resources.md │ │ └── talks.md │ ├── setup.md │ ├── troubleshooting │ │ ├── learn-ts.md │ │ ├── non-ts-files.md │ │ ├── official-typings-bugs.md │ │ ├── operators.md │ │ ├── ts-config.md │ │ ├── types.md │ │ └── utilities.md │ └── useful-hooks.md ├── hoc │ ├── excluding-props.md │ ├── full-example.md │ ├── index.md │ └── react-hoc-docs.md └── migration │ ├── from-flow.md │ ├── from-js.md │ ├── index.md │ └── js-docs.md ├── genReadme.js ├── netlify.toml ├── package.json ├── website ├── README.md ├── docusaurus.config.js ├── package.json ├── sidebars.json ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ ├── help.js │ │ ├── index.js │ │ └── users.js ├── static │ └── img │ │ ├── favicon.ico │ │ ├── icon.png │ │ ├── oss_logo.png │ │ ├── undraw_code_review.svg │ │ ├── undraw_monitor.svg │ │ ├── undraw_note_list.svg │ │ ├── undraw_online.svg │ │ ├── undraw_open_source.svg │ │ ├── undraw_operating_system.svg │ │ ├── undraw_react.svg │ │ ├── undraw_tweetstorm.svg │ │ └── undraw_youtube_tutorial.svg └── yarn.lock └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | "CONTRIBUTORS.md", 4 | "README.md" 5 | ], 6 | "imageSize": 100, 7 | "commit": true, 8 | "commitConvention": "angular", 9 | "contributors": [ 10 | { 11 | "login": "ferdaber", 12 | "name": "Ferdy Budhidharma", 13 | "avatar_url": "https://avatars2.githubusercontent.com/u/12239873?v=4", 14 | "profile": "https://github.com/ferdaber", 15 | "contributions": [ 16 | "review", 17 | "maintenance", 18 | "content" 19 | ] 20 | }, 21 | { 22 | "login": "sw-yx", 23 | "name": "swyx", 24 | "avatar_url": "https://avatars1.githubusercontent.com/u/6764957?v=4", 25 | "profile": "https://twitter.com/swyx", 26 | "contributions": [ 27 | "ideas", 28 | "review", 29 | "maintenance", 30 | "content", 31 | "question" 32 | ] 33 | }, 34 | { 35 | "login": "eps1lon", 36 | "name": "Sebastian Silbermann", 37 | "avatar_url": "https://avatars3.githubusercontent.com/u/12292047?v=4", 38 | "profile": "https://github.com/eps1lon", 39 | "contributions": [ 40 | "review", 41 | "maintenance", 42 | "content" 43 | ] 44 | }, 45 | { 46 | "login": "Attrash-Islam", 47 | "name": "Islam Attrash", 48 | "avatar_url": "https://avatars0.githubusercontent.com/u/7091543?v=4", 49 | "profile": "https://www.linkedin.com/in/islam-attrash-46703a94/", 50 | "contributions": [ 51 | "maintenance", 52 | "content" 53 | ] 54 | }, 55 | { 56 | "login": "stephenkoo", 57 | "name": "Stephen Koo", 58 | "avatar_url": "https://avatars2.githubusercontent.com/u/18624246?v=4", 59 | "profile": "https://stephenkoo.github.io/", 60 | "contributions": [ 61 | "question", 62 | "example" 63 | ] 64 | }, 65 | { 66 | "login": "andreasgruenh", 67 | "name": "Andreas", 68 | "avatar_url": "https://avatars2.githubusercontent.com/u/12122390?v=4", 69 | "profile": "https://github.com/andreasgruenh", 70 | "contributions": [ 71 | "code", 72 | "doc", 73 | "infra" 74 | ] 75 | }, 76 | { 77 | "login": "arvindcheenu", 78 | "name": "Arvind Srinivasan", 79 | "avatar_url": "https://avatars2.githubusercontent.com/u/13925213?v=4", 80 | "profile": "https://github.com/arvindcheenu", 81 | "contributions": [ 82 | "code", 83 | "content", 84 | "doc", 85 | "maintenance" 86 | ] 87 | }, 88 | { 89 | "login": "williamrjribeiro", 90 | "name": "William R. J. Ribeiro", 91 | "avatar_url": "https://avatars2.githubusercontent.com/u/1503499?v=4", 92 | "profile": "http://www.williamrjribeiro.com", 93 | "contributions": [ 94 | "ideas" 95 | ] 96 | }, 97 | { 98 | "login": "alexandrudanpop", 99 | "name": "Alexandru-Dan Pop", 100 | "avatar_url": "https://avatars0.githubusercontent.com/u/15979292?v=4", 101 | "profile": "https://alexandrudanpop.dev/", 102 | "contributions": [ 103 | "doc" 104 | ] 105 | }, 106 | { 107 | "login": "mastorm", 108 | "name": "Mathias Storm", 109 | "avatar_url": "https://avatars1.githubusercontent.com/u/10759336?v=4", 110 | "profile": "https://github.com/mastorm", 111 | "contributions": [ 112 | "doc" 113 | ] 114 | }, 115 | { 116 | "login": "dance2die", 117 | "name": "Sung M. Kim", 118 | "avatar_url": "https://avatars1.githubusercontent.com/u/8465237?v=4", 119 | "profile": "https://twitter.com/dance2die", 120 | "contributions": [ 121 | "doc", 122 | "ideas" 123 | ] 124 | }, 125 | { 126 | "login": "ryota-murakami", 127 | "name": "Ryota Murakami", 128 | "avatar_url": "https://avatars2.githubusercontent.com/u/5501268?v=4", 129 | "profile": "https://ryota-murakami.github.io/", 130 | "contributions": [ 131 | "example", 132 | "doc" 133 | ] 134 | }, 135 | { 136 | "login": "arpi17", 137 | "name": "Árpád Illyés", 138 | "avatar_url": "https://avatars1.githubusercontent.com/u/13800404?v=4", 139 | "profile": "https://github.com/arpi17", 140 | "contributions": [ 141 | "code" 142 | ] 143 | }, 144 | { 145 | "login": "xamgore", 146 | "name": "Igor Strebezhev", 147 | "avatar_url": "https://avatars3.githubusercontent.com/u/4586392?v=4", 148 | "profile": "https://twitter.com/xamgore", 149 | "contributions": [ 150 | "ideas", 151 | "doc" 152 | ] 153 | }, 154 | { 155 | "login": "dwjohnston", 156 | "name": "David Johnston", 157 | "avatar_url": "https://avatars2.githubusercontent.com/u/2467377?v=4", 158 | "profile": "https://geoplanets.io", 159 | "contributions": [ 160 | "doc" 161 | ] 162 | }, 163 | { 164 | "login": "kripod", 165 | "name": "Kristóf Poduszló", 166 | "avatar_url": "https://avatars3.githubusercontent.com/u/14854048?v=4", 167 | "profile": "https://github.com/kripod", 168 | "contributions": [ 169 | "doc" 170 | ] 171 | }, 172 | { 173 | "login": "glaschu1", 174 | "name": "james glasgow", 175 | "avatar_url": "https://avatars3.githubusercontent.com/u/10838852?v=4", 176 | "profile": "http://www.novusstudio.com/", 177 | "contributions": [ 178 | "doc" 179 | ] 180 | }, 181 | { 182 | "login": "Winner95", 183 | "name": "Ivan", 184 | "avatar_url": "https://avatars0.githubusercontent.com/u/13730032?v=4", 185 | "profile": "https://www.linkedin.com/in/iigrekov/", 186 | "contributions": [ 187 | "code" 188 | ] 189 | }, 190 | { 191 | "login": "selrond", 192 | "name": "Sebastian Andil", 193 | "avatar_url": "https://avatars1.githubusercontent.com/u/6603389?v=4", 194 | "profile": "http://sebastianandil.com", 195 | "contributions": [ 196 | "doc" 197 | ] 198 | }, 199 | { 200 | "login": "adnanhusain15", 201 | "name": "Adnan Husain", 202 | "avatar_url": "https://avatars2.githubusercontent.com/u/36721076?v=4", 203 | "profile": "https://github.com/adnanhusain15", 204 | "contributions": [ 205 | "doc" 206 | ] 207 | }, 208 | { 209 | "login": "artola", 210 | "name": "martin", 211 | "avatar_url": "https://avatars0.githubusercontent.com/u/11500763?v=4", 212 | "profile": "https://github.com/artola", 213 | "contributions": [ 214 | "doc" 215 | ] 216 | } 217 | ], 218 | "contributorsPerLine": 7, 219 | "projectName": "react", 220 | "projectOwner": "typescript-cheatsheets", 221 | "repoType": "github", 222 | "repoHost": "https://github.com", 223 | "skipCi": true 224 | } 225 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/advanced-cheatsheet.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Advanced Cheatsheet 3 | about: Report Issue/Suggest an idea for Advanced Cheatsheet 4 | title: "[Advanced] ISSUE_TITLE_HERE" 5 | labels: ADVANCED 6 | assignees: "" 7 | --- 8 | 9 | **What cheatsheet is this about? (if applicable)** 10 | 11 | Advanced cheatsheet 12 | 13 | **What's your issue or idea?** 14 | 15 | _Write here_ 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/basic-cheatsheet.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Basic Cheatsheet 3 | about: Report Issue/Suggest an idea for Basic Cheatsheet 4 | title: "[Basic] ISSUE_TITLE_HERE" 5 | labels: BASIC 6 | assignees: sw-yx 7 | --- 8 | 9 | **What cheatsheet is this about? (if applicable)** 10 | 11 | Basic cheatsheet 12 | 13 | **What's your issue or idea?** 14 | 15 | _Write here_ 16 | 17 | **(If applicable) Reproduction of issue in TypeScript Playground** 18 | 19 | _[start with the basic TS + React template](https://www.typescriptlang.org/play/?jsx=2&esModuleInterop=true&q=222#example/typescript-with-react)_ 20 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-react-ts-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General React+TS Question 3 | about: Questions are welcome! We want to know and solve your pain points. 4 | title: "[Question] QUESTION_TITLE_HERE" 5 | labels: good first issue 6 | assignees: sw-yx 7 | --- 8 | 9 | **(If applicable) Reproduction of issue in TypeScript Playground** 10 | 11 | _[start with the basic TS + React template](https://www.typescriptlang.org/play/?jsx=2&esModuleInterop=true&q=222#example/typescript-with-react)_ 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/hoc-cheatsheet.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: HOC Cheatsheet 3 | about: Report Issue/Suggest an idea for HOC Cheatsheet 4 | title: "[HOC] ISSUE_TITLE_HERE" 5 | labels: HOC 6 | assignees: "" 7 | --- 8 | 9 | **What cheatsheet is this about? (if applicable)** 10 | 11 | HOC cheatsheet 12 | 13 | **What's your issue or idea?** 14 | 15 | _Write here_ 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/migrating-cheatsheet.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Migrating Cheatsheet 3 | about: Report Issue/Suggest an idea for Migrating Cheatsheet 4 | title: "[Migrating] ISSUE_TITLE_HERE" 5 | labels: MIGRATING 6 | assignees: "" 7 | --- 8 | 9 | **What cheatsheet is this about? (if applicable)** 10 | 11 | Migrating cheatsheet 12 | 13 | **What's your issue or idea?** 14 | 15 | _Write here_ 16 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Thanks for contributing! 2 | 3 | - **If you are making a significant PR**, please make sure there is an open issue first 4 | - **If you're just fixing typos or adding small notes**, a brief explanation of why you'd like to add it would be nice :) 5 | 6 | **If you are contributing to README.md, DON'T**. README.md is auto-generated. Please just make edits to the source inside of `/docs/basic`. _Sorry, we get that it is a slight pain._ 7 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 60 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - pinned 8 | - security 9 | # Label to use when marking an issue as stale 10 | staleLabel: wontfix 11 | # Comment to post when marking an issue as stale. Set to `false` to disable 12 | markComment: > 13 | This issue has been automatically marked as stale because it has not had 14 | recent activity. It will be closed if no further activity occurs. Thank you 15 | for your contributions! 16 | # Comment to post when closing a stale issue. Set to `false` to disable 17 | closeComment: false 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: FormatCodeAndShowDiff 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | jobs: 9 | prettier: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout commit 13 | uses: actions/checkout@v2 14 | if: ${{ github.event_name == 'push' }} 15 | 16 | - name: Checkout Pull Request 17 | uses: actions/checkout@v2 18 | with: 19 | # TODO: Can we unify the checkout steps i.e. does this value resolve to "undefined" on `push`? 20 | ref: ${{ github.event.pull_request.head.sha }} 21 | if: ${{ github.event_name == 'pull_request' }} 22 | 23 | - name: Get yarn cache directory 24 | id: yarn 25 | run: echo "::set-output name=dir::$(yarn cache dir)" 26 | 27 | - name: Restore yarn cache 28 | uses: actions/cache@v1.1.2 29 | with: 30 | path: ${{ steps.yarn.outputs.dir }} 31 | key: ${{ runner.os }}-yarn-${{ hashFiles(format('{0}{1}', github.workspace, '/website/yarn.lock')) }} 32 | 33 | - name: Install dependencies 34 | run: yarn install --frozen-lockfile 35 | 36 | - name: '`yarn format` changes committed?' 37 | run: yarn format:check 38 | -------------------------------------------------------------------------------- /.github/workflows/mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliveStatusCodes": [200, 429] 3 | } 4 | -------------------------------------------------------------------------------- /.github/workflows/readme.js.yml: -------------------------------------------------------------------------------- 1 | name: Generate README 2 | on: 3 | workflow_dispatch: 4 | pull_request: 5 | paths: "docs/basic/*" 6 | branches: [main] 7 | push: 8 | paths: "docs/basic/*" 9 | branches: [main] 10 | jobs: 11 | build: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - uses: franzdiebold/github-env-vars-action@v2.1.0 16 | 17 | - name: Setup Node.js Environment 18 | uses: actions/setup-node@v1 19 | 20 | - name: Install dependencies 21 | run: npm i 22 | 23 | - name: Generate README from /docs/basic 24 | run: npm run gen-readme --if-present 25 | env: 26 | ENV_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Snowpack dependency directory (https://snowpack.dev/) 45 | web_modules/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | .parcel-cache 78 | 79 | # Next.js build output 80 | .next 81 | 82 | # Nuxt.js build / generate output 83 | .nuxt 84 | dist 85 | 86 | # Gatsby files 87 | .cache/ 88 | # Comment in the public line in if your project uses Gatsby and not Next.js 89 | # https://nextjs.org/blog/next-9-1#public-directory-support 90 | # public 91 | 92 | # vuepress build output 93 | .vuepress/dist 94 | 95 | # Serverless directories 96 | .serverless/ 97 | 98 | # FuseBox cache 99 | .fusebox/ 100 | 101 | # DynamoDB Local files 102 | .dynamodb/ 103 | 104 | # TernJS port file 105 | .tern-port 106 | 107 | # Stores VSCode versions used for testing VSCode extensions 108 | .vscode-test 109 | 110 | # Idea / Jetbrains IDE 111 | .idea 112 | 113 | # yarn v2 114 | 115 | .yarn/cache 116 | .yarn/unplugged 117 | .yarn/build-state.yml 118 | .pnp.* 119 | 120 | # Miscellaneous 121 | .history 122 | .DS_Store 123 | 124 | # Docusaurus files 125 | website/build 126 | website/.docusaurus 127 | website/.cache-loader 128 | 129 | # copy of contributors for the website 130 | docs/contributors.md 131 | docs/contributing.md 132 | 133 | # Local Netlify folder 134 | .netlify 135 | 136 | # Lock-files 137 | package-lock.json 138 | yarn.lock -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # So you want to contribute! 2 | 3 | Thanks for helping out the community! We are actively looking for contributors and maintainers so you are more than welcome. 4 | 5 | I thought I should lay out some core principles that we will follow so that this repo doesn't get too wild and lose its value. 6 | 7 | 1. **We are a CHEATSHEET above all**: all examples to be as simple as possible, easily searched, and presented for copy-and-paste. 8 | 2. **Collapsible explanations**: No more than 1-2 sentences of explanation, any more than that we put inside `details` tags. 9 | 3. **React + TypeScript ONLY**: React's ecosystem is huge, we can't possibly cover it all. This includes Redux. Would encourage people to maintain separate lists for stuff like React + Apollo Graphql, for example. Also we make no attempt to convince people to use TypeScript, we only exist to help people who have already decided to try it out. 10 | 4. **Add TypeScript Playground Links**: Whenever adding a code example longer than four lines, add a link to the TypeScript Playground with the code. Use the default compiler Playground options. 11 | 12 | That's all I've got! Again, really happy you are thinking about helping out, who knows, the person who you might be helping is yourself in future! 13 | 14 | ## Project structure 15 | 16 | - All content is in `/docs` 17 | - the `/docs/basic` is compiled into `README.md` to preserve GitHub readability via GitHub action, thanks 18 | - `/website` consumes the `/docs` content, which is a [Docusaurus 2](https://docusaurus.io/) site, which also has [Algolia search](https://www.algolia.com/) (thanks to both teams for their support!) 19 | 20 | The website is deployed to Netlify on swyx's personal account. 21 | 22 | To run the docsite locally: 23 | 24 | ```bash 25 | yarn # install deps 26 | ## make sure deps are installed in /website too 27 | cd website && yarn start 28 | ``` 29 | 30 | example output from successful startup 31 | 32 | ``` 33 | yarn run v1.22.4 34 | warning package.json: No license field 35 | $ docusaurus start 36 | Starting the development server... 37 | 38 | ✔ Client 39 | Compiled successfully in 9.61s 40 | 41 | ℹ 「wds」: Project is running at http://localhost:3000/ 42 | ℹ 「wds」: webpack output is served from / 43 | ℹ 「wds」: Content not from webpack is served from /Users/wanshawn/Work/react-typescript-cheatsheet/website 44 | ℹ 「wds」: 404s will fallback to /index.html 45 | 46 | ✔ Client 47 | Compiled successfully in 116.41ms 48 | ``` 49 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ## Contributors 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |

Ferdy Budhidharma

👀 🚧 🖋

swyx

🤔 👀 🚧 🖋 💬

Sebastian Silbermann

👀 🚧 🖋

Islam Attrash

🚧 🖋

Stephen Koo

💬 💡

Andreas

💻 📖 🚇

Arvind Srinivasan

💻 🖋 📖 🚧

William R. J. Ribeiro

🤔

Alexandru-Dan Pop

📖

Mathias Storm

📖

Sung M. Kim

📖 🤔

Ryota Murakami

💡 📖

Árpád Illyés

💻

Igor Strebezhev

🤔 📖

David Johnston

📖

Kristóf Poduszló

📖

james glasgow

📖

Ivan

💻

Sebastian Andil

📖

Adnan Husain

📖

martin

📖
35 | 36 | 37 | 38 | 39 | 40 | 41 | Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): 42 | 43 | 44 | 45 | 46 | 47 | This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 shawn wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /copyFile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const filesTopCopy = [ 3 | { 4 | src: "../CONTRIBUTORS.md", 5 | dest: "../docs/contributors.md", 6 | hideHeader: true, 7 | }, 8 | { 9 | src: "../CONTRIBUTING.md", 10 | dest: "../docs/contributing.md", 11 | hideHeader: true, 12 | }, 13 | ]; 14 | 15 | const generatingPageOptions = `--- 16 | hide_title: true 17 | --- 18 | 19 | `; 20 | 21 | function writeNewFile(src, dest) { 22 | const fileContent = fs.readFileSync(src).toString(); 23 | const data = new Uint8Array(Buffer.from(generatingPageOptions + fileContent)); 24 | 25 | fs.writeFile(dest, data, (err) => { 26 | if (err) { 27 | console.log("Error Found:", err); 28 | } else { 29 | console.log("Files added"); 30 | } 31 | }); 32 | } 33 | 34 | function copyFile(src, dest) { 35 | fs.copyFile(src, dest, (err) => { 36 | if (err) { 37 | console.log("Error Found:", err); 38 | } else { 39 | console.log("Files copied"); 40 | } 41 | }); 42 | } 43 | 44 | filesTopCopy.forEach(({ src, dest, hideHeader }) => { 45 | if (hideHeader) { 46 | writeNewFile(src, dest); 47 | } else { 48 | copyFile(src, dest); 49 | } 50 | }); 51 | -------------------------------------------------------------------------------- /docs/advanced/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: intro 3 | sidebar_label: Intro 4 | title: Advanced Cheatsheet 5 | --- 6 | 7 | **This Advanced Cheatsheet** helps show and explain advanced usage of generic types for people writing reusable type utilities/functions/render prop/higher order components and TS+React **libraries**. 8 | 9 | - It also has miscellaneous tips and tricks for pro users. 10 | - Advice for contributing to DefinitelyTyped 11 | - The goal is to take _full advantage_ of TypeScript. 12 | 13 | **Creating React + TypeScript Libraries** 14 | 15 | The best tool for creating React + TS libraries right now is [`tsdx`](https://github.com/palmerhq/tsdx). Run `npx tsdx create` and select the "react" option. You can view [the React User Guide](https://github.com/palmerhq/tsdx/issues/5) for a few tips on React+TS library best practices and optimizations for production. 16 | 17 | Another option is [Rollpkg](https://github.com/rafgraph/rollpkg), which uses Rollup and the TypeScript compiler (not Babel) to create packages. It includes default configs for TypeScript, Prettier, ESLint, and Jest (setup for use with React), as well as Bundlephobia package stats for each build. 18 | 19 | - Be sure to also check [`basarat`'s guide](https://basarat.gitbooks.io/typescript/content/docs/quick/library.html) for library tsconfig settings. 20 | - Alec Larson: [The best Rollup config for TypeScript libraries](https://gist.github.com/aleclarson/9900ed2a9a3119d865286b218e14d226) 21 | - From the Angular world, check out https://github.com/bitjson/typescript-starter 22 | -------------------------------------------------------------------------------- /docs/advanced/misc-concerns.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: misc_concerns 3 | title: "Section 3: Misc. Concerns" 4 | sidebar_label: Misc. Concerns 5 | --- 6 | 7 | Sometimes writing React isn't just about React. While we don't focus on other libraries like Redux (see below for more on that), here are some tips on other common concerns when making apps with React + TypeScript. 8 | 9 | ## Writing TypeScript Libraries instead of Apps 10 | 11 | `propTypes` may seem unnecessary with TypeScript, especially when building React + TypeScript **apps**, but they are still relevant when writing **libraries** which may be used by developers working in Javascript. 12 | 13 | ```ts 14 | interface IMyComponentProps { 15 | autoHeight: boolean; 16 | secondProp: number; 17 | } 18 | 19 | export class MyComponent extends React.Component { 20 | static propTypes = { 21 | autoHeight: PropTypes.bool, 22 | secondProp: PropTypes.number.isRequired, 23 | }; 24 | } 25 | ``` 26 | 27 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 28 | 29 | ## Commenting Components 30 | 31 | TypeScript uses [TSDoc](https://github.com/Microsoft/tsdoc), a variant of JSDoc for TypeScript. This is very handy for writing component libraries and having useful descriptions pop up in autocomplete and other tooling (like the [Docz PropsTable](https://www.docz.site/docs/components-api#propstable)). The main thing to remember is to use `/** YOUR_COMMENT_HERE */` syntax in the line just above whatever you're annotating. 32 | 33 | ```tsx 34 | import React from "react"; 35 | 36 | interface MyProps { 37 | /** Description of prop "label". 38 | * @default foobar 39 | * */ 40 | label?: string; 41 | } 42 | 43 | /** 44 | * General component description in JSDoc format. Markdown is *supported*. 45 | */ 46 | export default function MyComponent({ label = "foobar" }: MyProps) { 47 | return
Hello world {label}
; 48 | } 49 | ``` 50 | 51 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) 52 | 53 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 54 | 55 | ## Namespaced Components 56 | 57 | Often when creating similar components or components that have a parent-child relationship, it is useful to namespace your components. Types can easily be added be using `Object.assign()`; 58 | 59 | ```tsx 60 | import React from "react"; 61 | 62 | const Input = (props: any) => ; 63 | 64 | const Form = React.forwardRef( 65 | ({ children, ...otherProps }, ref) => ( 66 |
67 | {children} 68 |
69 | ) 70 | ); 71 | 72 | /** 73 | * Exported components now can be used as `
` and `` 74 | */ 75 | export default Object.assign(Form, { Input: Input }); 76 | ``` 77 | 78 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2&ssl=1&ssc=1&pln=14&pc=52#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BJGsAV3gF44AKMHMOgC44KGgE8AlHA4A+OAB5gLdnADeAOk18IAgL5wA9DIpVaDOADFoeLsnQx1maAHcUUACbJM8gBIAVAFkAGQARYAA3AFEAGyQQJBoYABoRcRlublU0AAtgaPciGhTNdQgYbKQoAAV+Ol0UokwpWR4KOAUnKDwNTTKK6tr9Ro5VRt1jcnb2rNz8wt02hQNOkAmJCQBuE3IDACpdtt24SIAPSFgkdzhqcFoEmDo4Gghna9E4ACMkOFY6S5FHgADeRWLoyQGpK7A0EgdTMNgwcGHAwUJBnaDwdxITAoVjReAAeQ+ACskBh1Cg6HRgABzGjcGEpVTw9jCFkwXSbIA) 79 | 80 | (Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165)) 81 | 82 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 83 | 84 | ## Design System Development 85 | 86 | I do like [Docz](https://docz.site/) which takes basically [1 line of config](https://www.docz.site/documentation/project-configuration#typescript) to accept TypeScript. However it is newer and has a few more rough edges (many breaking changes since it is still < v1.0) 87 | 88 | For developing with Storybook, read the docs I wrote over here: . This includes automatic proptype documentation generation, which is awesome :) 89 | 90 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 91 | 92 | ## Migrating From Flow 93 | 94 | You should check out large projects that are migrating from flow to pick up concerns and tips: 95 | 96 | - [Jest](https://github.com/facebook/jest/pull/7554) 97 | - [Expo](https://github.com/expo/expo/issues/2164) 98 | - [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982) 99 | - [Storybook](https://github.com/storybooks/storybook/issues/5030) 100 | - [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf) 101 | 102 | Useful libraries: 103 | 104 | - 105 | - 106 | - 107 | 108 | If you have specific advice in this area, please file a PR! 109 | 110 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 111 | 112 | ## Prettier 113 | 114 | There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! 115 | 116 | ```bash 117 | $ yarn add -D prettier husky lint-staged 118 | ``` 119 | 120 | ```json 121 | // inside package.json 122 | { 123 | //... 124 | "husky": { 125 | "hooks": { 126 | "pre-commit": "lint-staged" 127 | } 128 | }, 129 | "lint-staged": { 130 | "linters": { 131 | "src/*.{ts,tsx,js,jsx,css,scss,md}": [ 132 | "prettier --trailing-comma es5 --single-quote --write", 133 | "git add" 134 | ], 135 | "ignore": ["**/dist/*, **/node_modules/*"] 136 | } 137 | }, 138 | "prettier": { 139 | "printWidth": 80, 140 | "semi": false, 141 | "singleQuote": true, 142 | "trailingComma": "es5" 143 | } 144 | } 145 | ``` 146 | 147 | Integrating this with ESlint may be a problem. We haven't written much on this yet, please contribute if you have a strong opinion. [Here's a helpful gist.](https://gist.github.com/JirkaVebr/519c7597517e4ba756d5b89e7cb4cc0e) 148 | 149 | For library authors, this is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). You may also wish to check out the newer https://ts-engine.dev/ project. 150 | 151 | ## Testing 152 | 153 | Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions: 154 | 155 | - https://github.com/azz/jest-runner-tsc 156 | - https://github.com/SamVerschueren/tsd 157 | - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) 158 | - https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)) 159 | 160 | ## Working with Non-TypeScript Libraries (writing your own index.d.ts) 161 | 162 | Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: 163 | 164 | ``` 165 | [ts] 166 | Could not find a declaration file for module 'de-indent'. '/Users/swyx/Work/react-sfc-loader/node_modules/de-indent/index.js' implicitly has an 'any' type. 167 | Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016] 168 | ``` 169 | 170 | So create a `.d.ts` file anywhere in your project with the module definition: 171 | 172 | ```ts 173 | // de-indent.d.ts 174 | declare module "de-indent" { 175 | function deindent(): void; 176 | export = deindent; // default export 177 | } 178 | ``` 179 | 180 |
181 | 182 | Further Discussion 183 | 184 | Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/8). We have more discussion and examples [in our issue here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12). 185 | 186 |
187 | 188 | ## Compilation Speed 189 | 190 | Compiling large TS projects can get slow. Here are some tips: 191 | 192 | - We have a dedicated repo tracking TS speed recommendations: https://github.com/typescript-cheatsheets/speed 193 | - Use [TS 3.0 Project references](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_version#typescript-30) 194 | - Check the official [TS performance wiki guidelines](https://github.com/microsoft/TypeScript/wiki/Performance) - note that [Dan Rossenwasser says to take it with a grain of salt](https://news.ycombinator.com/item?id=25199070) 195 | - Webpack ([see CRA diff](https://gist.github.com/jaredpalmer/d3016701589f14df8a3572df91a5754b)): 196 | - set `output.pathinfo = false` 197 | - set `optimization.splitChunks`, `optimization.removeAvailableModules`, `optimization.removeEmptyChunks` to `false` 198 | -------------------------------------------------------------------------------- /docs/advanced/types-react-ap.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: types_react_api 3 | title: "Section 4: @types/react and @types/react-dom APIs" 4 | sidebar_label: "@types/react and @types/react-dom APIs" 5 | --- 6 | 7 | The `@types` typings export both "public" types meant for your use as well as "private" types that are for internal use. 8 | 9 | Check [SaltyCrane's React TypeScript Cheatsheet](https://github.com/saltycrane/typescript-cheatsheet) for a nice autogenerated complete reference. 10 | 11 | ## `@types/react` 12 | 13 | [Link to `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts) 14 | 15 | **Namespace: React** 16 | 17 | Most Commonly Used Interfaces and Types 18 | 19 | - `ReactNode` - anything that is renderable _inside_ of JSX, this is NOT the same as what can be rendered by a component! 20 | - `Component` - base class of all class-based components 21 | - `PureComponent` - base class for all class-based optimized components 22 | - `FC`, `FunctionComponent` - a complete interface for function components, often used to type external components instead of typing your own 23 | - `CSSProperties` - used to type style objects 24 | - all events: used to type event handlers 25 | - all event handlers: used to type event handlers 26 | - all consts: `Children`, `Fragment`, ... are all public and reflect the React runtime namespace 27 | 28 | Not Commonly Used but Good to know 29 | 30 | - `Ref` - used to type `innerRef` 31 | - `ElementType` - used for higher order components or operations on components, e.g. [Polymorphic Components](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase#polymorphic-components) 32 | - `ReactElement` - [can be used if you want to pass it to `cloneElement`](https://www.reddit.com/r/reactjs/comments/ia8sdi/any_other_typescript_users_constantly_confused/g1npahe/) aka it's pretty rarely used 33 | - `ComponentType` - used for higher order components where you don't specifically deal with the intrinsic components 34 | - `ReactPortal` - used if you specifically need to type a prop as a portal, otherwise it is part of `ReactNode` 35 | - `ComponentClass` - a complete interface for the produced constructor function of a class declaration that extends `Component`, often used to type external components instead of typing your own 36 | - `JSXElementConstructor` - anything that TypeScript considers to be a valid thing that can go into the opening tag of a JSX expression 37 | - `ComponentProps` - props of a component - most useful for [Wrapping/Mirroring a HTML Element](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase#wrappingmirroring-a-html-element) 38 | - `ComponentPropsWithRef` - props of a component where if it is a class-based component it will replace the `ref` prop with its own instance type 39 | - `ComponentPropsWithoutRef` - props of a component without its `ref` prop 40 | - `HTMLProps` and `HTMLAttributes` - these are the most generic versions, for global attributes (see a list of [attributes marked as "global attribute" on MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes)). In general, prefer `React.ComponentProps`, `JSX.IntrinsicElements`, or [specialized HTMLAttributes interfaces](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a2aa0406e7bf269eef01292fcb2b24dee89a7d2b/types/react/index.d.ts#L1914-L2625): 41 | 42 |
43 | 44 | List of specialized HTMLAttributes 45 | 46 | 47 | Note that there are about 50 of these, which means there are some HTML elements which are not covered. 48 | 49 | - `AnchorHTMLAttributes` 50 | - `AudioHTMLAttributes` 51 | - `AreaHTMLAttributes` 52 | - `BaseHTMLAttributes` 53 | - `BlockquoteHTMLAttributes` 54 | - `ButtonHTMLAttributes` 55 | - `CanvasHTMLAttributes` 56 | - `ColHTMLAttributes` 57 | - `ColgroupHTMLAttributes` 58 | - `DataHTMLAttributes` 59 | - `DetailsHTMLAttributes` 60 | - `DelHTMLAttributes` 61 | - `DialogHTMLAttributes` 62 | - `EmbedHTMLAttributes` 63 | - `FieldsetHTMLAttributes` 64 | - `FormHTMLAttributes` 65 | - `HtmlHTMLAttributes` 66 | - `IframeHTMLAttributes` 67 | - `ImgHTMLAttributes` 68 | - `InsHTMLAttributes` 69 | - `InputHTMLAttributes` 70 | - `KeygenHTMLAttributes` 71 | - `LabelHTMLAttributes` 72 | - `LiHTMLAttributes` 73 | - `LinkHTMLAttributes` 74 | - `MapHTMLAttributes` 75 | - `MenuHTMLAttributes` 76 | - `MediaHTMLAttributes` 77 | - `MetaHTMLAttributes` 78 | - `MeterHTMLAttributes` 79 | - `QuoteHTMLAttributes` 80 | - `ObjectHTMLAttributes` 81 | - `OlHTMLAttributes` 82 | - `OptgroupHTMLAttributes` 83 | - `OptionHTMLAttributes` 84 | - `OutputHTMLAttributes` 85 | - `ParamHTMLAttributes` 86 | - `ProgressHTMLAttributes` 87 | - `SlotHTMLAttributes` 88 | - `ScriptHTMLAttributes` 89 | - `SelectHTMLAttributes` 90 | - `SourceHTMLAttributes` 91 | - `StyleHTMLAttributes` 92 | - `TableHTMLAttributes` 93 | - `TextareaHTMLAttributes` 94 | - `TdHTMLAttributes` 95 | - `ThHTMLAttributes` 96 | - `TimeHTMLAttributes` 97 | - `TrackHTMLAttributes` 98 | - `VideoHTMLAttributes` 99 | - `WebViewHTMLAttributes` 100 | 101 |
102 | 103 | - all methods: `createElement`, `cloneElement`, ... are all public and reflect the React runtime API 104 | 105 | [@Ferdaber's note](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69): I discourage the use of most `...Element` types because of how black-boxy `JSX.Element` is. You should almost always assume that anything produced by `React.createElement` is the base type `React.ReactElement`. 106 | 107 | **Namespace: JSX** 108 | 109 | - `Element` - the type of any JSX expression. You should ideally never need to see or use this, but you do because of [a limitation of TypeScript](https://github.com/microsoft/TypeScript/issues/21699). 110 | - `LibraryManagedAttributes` - It specifies other places where JSX elements can declare and initialize property types. Used to resolve static `defaultProps` and `propTypes` with the internal props type of a component. 111 | - `IntrinsicElements` - every possible built-in component that can be typed in as a lowercase tag name in JSX. If you're using this to get the attributes for a HTML element, `React.ComponentProps` may be more readable as it doesn't require knowing what "Intrinsic" means. 112 | 113 | Not commonly used but good to know 114 | 115 | - `IntrinsicAttributes` set of attributes that all `IntrinsicElements` support... basically just `key`. 116 | - `ElementChildrenAttribute` name of property that TS looks at to figure out what types of children a component supports. Basically the `children` property 117 | - `ElementAttributesProperty` name of property that TS looks at to figure out what attributes a component supports. Basically the `props` property (for a class instance) 118 | 119 | **Don't use/Internal/Deprecated** 120 | 121 | Anything not listed above is considered an internal type and not public. If you're not sure you can check out the source of `@types/react`. The types are annotated accordingly. 122 | 123 | - `SFCElement` 124 | - `SFC` 125 | - `ComponentState` 126 | - `LegacyRef` 127 | - `StatelessComponent` 128 | - `ReactType` 129 | 130 | ### Adding non-standard attributes 131 | 132 | The attributes allowed on host components such as `button` or `img` follow the 133 | [HTML living standard](https://html.spec.whatwg.org/). New features that are not yet part of the living standard 134 | or are only implemented by certain browsers will therefore cause a type error. If 135 | you specifically write code for these browsers or polyfill these attributes you can 136 | use [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to still get those components type checked without having 137 | to use `any` or `@ts-ignore`. 138 | 139 | In this example we'll add the [`loading`](https://www.chromestatus.com/feature/5645767347798016) attribute which adds support for [lazy-loading](https://web.dev/native-lazy-loading) images on Chrome: 140 | 141 | ```ts 142 | // react-unstable-attributes.d.ts 143 | import "react"; 144 | 145 | declare module "react" { 146 | interface ImgHTMLAttributes extends HTMLAttributes { 147 | loading?: "auto" | "eager" | "lazy"; 148 | } 149 | } 150 | ``` 151 | 152 | ## `@types/react-dom` 153 | 154 | To be written 155 | -------------------------------------------------------------------------------- /docs/advanced/utility-types.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: utility_types 3 | title: "Utility Types" 4 | sidebar_label: Utility Types 5 | --- 6 | 7 | We will assume knowledge of utility types covered in the sister project [`typescript-cheatsheets/utilities`](https://github.com/typescript-cheatsheets/utilities). Look up libraries included there as well for your typing needs. 8 | 9 | If you intend to maintain a large TS codebase/a nontrivial React+TS library, **we strongly recommend exploring these utilities** so that you don't reinvent the wheel and/or lose sanity trying to do so. Studying their code can also teach you a lot of advanced TS that is not covered here. 10 | 11 | I also recommend have a good working knowledge of how to construct the inbuilt utility types from scratch. See [Dr. Rasuchmeyer's guide](https://2ality.com/2020/06/computing-with-types.html) for a concise introduction. 12 | 13 | A level of comfort with **generic types** is therefore required. Here are some helpful resources: 14 | 15 | - https://ts.chibicode.com/generics/ 16 | -------------------------------------------------------------------------------- /docs/basic/editor-integration.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: editor_integration 3 | title: Editor Tooling and Integration 4 | --- 5 | 6 | - VSCode 7 | - swyx's VSCode Extension: https://github.com/sw-yx/swyx-react-typescript-snippets 8 | - amVim: https://marketplace.visualstudio.com/items?itemName=auiworks.amvim 9 | - VIM 10 | - https://github.com/Quramy/tsuquyomi 11 | - nvim-typescript? 12 | - https://github.com/leafgarland/typescript-vim 13 | - peitalin/vim-jsx-typescript 14 | - NeoVim: https://github.com/neoclide/coc.nvim 15 | - other discussion: https://mobile.twitter.com/ryanflorence/status/1085715595994095620 16 | 17 | You are free to use this repo's TSX logo if you wish: 18 | 19 | [![https://user-images.githubusercontent.com/6764957/53868378-2b51fc80-3fb3-11e9-9cee-0277efe8a927.png](https://user-images.githubusercontent.com/6764957/53868378-2b51fc80-3fb3-11e9-9cee-0277efe8a927.png)](https://user-images.githubusercontent.com/6764957/53868378-2b51fc80-3fb3-11e9-9cee-0277efe8a927.png) 20 | 21 | You may also wish to use alternative logos - [jsx-tsx-logos](https://github.com/Protectator/jsx-tsx-logos) 22 | 23 | ![https://github.com/Protectator/jsx-tsx-logos/raw/master/example.png](https://github.com/Protectator/jsx-tsx-logos/raw/master/example.png) 24 | -------------------------------------------------------------------------------- /docs/basic/examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: examples 3 | title: Example App 4 | sidebar_label: Examples 5 | --- 6 | 7 | - [Create React App TypeScript Todo Example 2021](https://github.com/laststance/create-react-app-typescript-todo-example-2021) 8 | - [Ben Awad's 14 hour Fullstack React/GraphQL/TypeScript Tutorial](https://www.youtube.com/watch?v=I6ypD7qv3Z8) 9 | - [Cypress Realworld App](https://github.com/cypress-io/cypress-realworld-app) 10 | -------------------------------------------------------------------------------- /docs/basic/getting-started/basic-type-examples.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: basic_type_example 3 | title: Typing Component Props 4 | --- 5 | 6 | This is intended as a basic orientation and reference for React developers familiarizing with TypeScript. 7 | 8 | ## Basic Prop Types Examples 9 | 10 | A list of TypeScript types you will likely use in a React+TypeScript app: 11 | 12 | ```tsx 13 | type AppProps = { 14 | message: string; 15 | count: number; 16 | disabled: boolean; 17 | /** array of a type! */ 18 | names: string[]; 19 | /** string literals to specify exact string values, with a union type to join them together */ 20 | status: "waiting" | "success"; 21 | /** any object as long as you dont use its properties (NOT COMMON but useful as placeholder) */ 22 | obj: object; 23 | obj2: {}; // almost the same as `object`, exactly the same as `Object` 24 | /** an object with any number of properties (PREFERRED) */ 25 | obj3: { 26 | id: string; 27 | title: string; 28 | }; 29 | /** array of objects! (common) */ 30 | objArr: { 31 | id: string; 32 | title: string; 33 | }[]; 34 | /** a dict object with any number of properties of the same type */ 35 | dict1: { 36 | [key: string]: MyTypeHere; 37 | }; 38 | dict2: Record; // equivalent to dict1 39 | /** any function as long as you don't invoke it (not recommended) */ 40 | onSomething: Function; 41 | /** function that doesn't take or return anything (VERY COMMON) */ 42 | onClick: () => void; 43 | /** function with named prop (VERY COMMON) */ 44 | onChange: (id: number) => void; 45 | /** alternative function type syntax that takes an event (VERY COMMON) */ 46 | onClick(event: React.MouseEvent): void; 47 | /** an optional prop (VERY COMMON!) */ 48 | optional?: OptionalType; 49 | }; 50 | ``` 51 | 52 | Notice we have used the TSDoc `/** comment */` style here on each prop. You can and are encouraged to leave descriptive comments on reusable components. For a fuller example and discussion, see our [Commenting Components](https://react-typescript-cheatsheet.netlify.app/docs/advanced/misc_concerns/#commenting-components) section in the Advanced Cheatsheet. 53 | 54 | ## Useful React Prop Type Examples 55 | 56 | Relevant for components that accept other React components as props. 57 | 58 | ```tsx 59 | export declare interface AppProps { 60 | children1: JSX.Element; // bad, doesnt account for arrays 61 | children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings 62 | children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility 63 | children4: React.ReactChild[]; // better, accepts array children 64 | children: React.ReactNode; // best, accepts everything (see edge case below) 65 | functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type 66 | style?: React.CSSProperties; // to pass through style props 67 | onChange?: React.FormEventHandler; // form events! the generic parameter is the type of event.target 68 | // more info: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring 69 | props: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its ref 70 | props2: Props & React.ComponentPropsWithRef; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref 71 | } 72 | ``` 73 | 74 |
75 | 76 | Small `React.ReactNode` edge case 77 | 78 | 79 | This code typechecks but has a runtime error: 80 | 81 | ```tsx 82 | type Props = { 83 | children: React.ReactNode; 84 | }; 85 | 86 | function Comp({ children }: Props) { 87 | return
{children}
; 88 | } 89 | function App() { 90 | return {{}}; // Runtime Error: Objects not valid as React Child! 91 | } 92 | ``` 93 | 94 | This is because `ReactNode` includes `ReactFragment` which allows a `{}` type, which is [too wide](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/37596#issue-480260937). Fixing this would break a lot of libraries, so for now you just have to be mindful that `ReactNode` is not absolutely bulletproof. 95 | 96 | [Thanks @pomle for raising this.](https://github.com/typescript-cheatsheets/react/issues/357) 97 | 98 |
99 | 100 |
101 | JSX.Element vs React.ReactNode? 102 | 103 | Quote [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57): A more technical explanation is that a valid React node is not the same thing as what is returned by `React.createElement`. Regardless of what a component ends up rendering, `React.createElement` always returns an object, which is the `JSX.Element` interface, but `React.ReactNode` is the set of all possible return values of a component. 104 | 105 | - `JSX.Element` -> Return value of `React.createElement` 106 | - `React.ReactNode` -> Return value of a component 107 | 108 |
109 | 110 | [More discussion: Where ReactNode does not overlap with JSX.Element](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/129) 111 | 112 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 113 | 114 | ## Types or Interfaces? 115 | 116 | You can use either Types or Interfaces to type Props and State, so naturally the question arises - which do you use? 117 | 118 | ### TL;DR 119 | 120 | Use Interface until You Need Type - [orta](https://twitter.com/orta/status/1356129195835973632?s=20). 121 | 122 | ### More Advice 123 | 124 | Here's a helpful rule of thumb: 125 | 126 | - always use `interface` for public API's definition when authoring a library or 3rd party ambient type definitions, as this allows a consumer to extend them via _declaration merging_ if some definitions are missing. 127 | 128 | - consider using `type` for your React Component Props and State, for consistency and because it is more constrained. 129 | 130 | You can read more about the reasoning behind this rule of thumb in [Interface vs Type alias in TypeScript 2.7](https://medium.com/@martin_hotell/interface-vs-type-alias-in-typescript-2-7-2a8f1777af4c). 131 | 132 | The TypeScript Handbook now also includes guidance on [Differences Between Type Aliases and Interfaces](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#differences-between-type-aliases-and-interfaces). 133 | 134 | > Note: At scale, there are performance reasons to prefer interfaces ([see official Microsoft notes on this](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections)) but [take this with a grain of salt](https://news.ycombinator.com/item?id=25201887) 135 | 136 | Types are useful for union types (e.g. `type MyType = TypeA | TypeB`) whereas Interfaces are better for declaring dictionary shapes and then `implementing` or `extending` them. 137 | 138 | ### Useful table for Types vs Interfaces 139 | 140 | It's a nuanced topic, don't get too hung up on it. Here's a handy table: 141 | 142 | | Aspect | Type | Interface | 143 | | ----------------------------------------------- | :--: | :-------: | 144 | | Can describe functions | ✅ | ✅ | 145 | | Can describe constructors | ✅ | ✅ | 146 | | Can describe tuples | ✅ | ✅ | 147 | | Interfaces can extend it | ⚠️ | ✅ | 148 | | Classes can extend it | 🚫 | ✅ | 149 | | Classes can implement it (`implements`) | ⚠️ | ✅ | 150 | | Can intersect another one of its kind | ✅ | ⚠️ | 151 | | Can create a union with another one of its kind | ✅ | 🚫 | 152 | | Can be used to create mapped types | ✅ | 🚫 | 153 | | Can be mapped over with mapped types | ✅ | ✅ | 154 | | Expands in error messages and logs | ✅ | 🚫 | 155 | | Can be augmented | 🚫 | ✅ | 156 | | Can be recursive | ⚠️ | ✅ | 157 | 158 | ⚠️ In some cases 159 | 160 | (source: [Karol Majewski](https://twitter.com/karoljmajewski/status/1082413696075382785)) 161 | 162 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 163 | -------------------------------------------------------------------------------- /docs/basic/getting-started/class-components.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: class_components 3 | title: Class Components 4 | --- 5 | 6 | Within TypeScript, `React.Component` is a generic type (aka `React.Component`), so you want to provide it with (optional) prop and state type parameters: 7 | 8 | ```tsx 9 | type MyProps = { 10 | // using `interface` is also ok 11 | message: string; 12 | }; 13 | type MyState = { 14 | count: number; // like this 15 | }; 16 | class App extends React.Component { 17 | state: MyState = { 18 | // optional second annotation for better type inference 19 | count: 0, 20 | }; 21 | render() { 22 | return ( 23 |
24 | {this.props.message} {this.state.count} 25 |
26 | ); 27 | } 28 | } 29 | ``` 30 | 31 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) 32 | 33 | Don't forget that you can export/import/extend these types/interfaces for reuse. 34 | 35 |
36 | Why annotate state twice? 37 | 38 | It isn't strictly necessary to annotate the `state` class property, but it allows better type inference when accessing `this.state` and also initializing the state. 39 | 40 | This is because they work in two different ways, the 2nd generic type parameter will allow `this.setState()` to work correctly, because that method comes from the base class, but initializing `state` inside the component overrides the base implementation so you have to make sure that you tell the compiler that you're not actually doing anything different. 41 | 42 | [See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). 43 | 44 |
45 | 46 |
47 | No need for readonly 48 | 49 | You often see sample code include `readonly` to mark props and state immutable: 50 | 51 | ```tsx 52 | type MyProps = { 53 | readonly message: string; 54 | }; 55 | type MyState = { 56 | readonly count: number; 57 | }; 58 | ``` 59 | 60 | This is not necessary as `React.Component` already marks them as immutable. ([See PR and discussion!](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813)) 61 | 62 |
63 | 64 | **Class Methods**: Do it like normal, but just remember any arguments for your functions also need to be typed: 65 | 66 | ```tsx 67 | class App extends React.Component<{ message: string }, { count: number }> { 68 | state = { count: 0 }; 69 | render() { 70 | return ( 71 |
this.increment(1)}> 72 | {this.props.message} {this.state.count} 73 |
74 | ); 75 | } 76 | increment = (amt: number) => { 77 | // like this 78 | this.setState((state) => ({ 79 | count: state.count + amt, 80 | })); 81 | }; 82 | } 83 | ``` 84 | 85 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) 86 | 87 | **Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: 88 | 89 | ```tsx 90 | class App extends React.Component<{ 91 | message: string; 92 | }> { 93 | pointer: number; // like this 94 | componentDidMount() { 95 | this.pointer = 3; 96 | } 97 | render() { 98 | return ( 99 |
100 | {this.props.message} and {this.pointer} 101 |
102 | ); 103 | } 104 | } 105 | ``` 106 | 107 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) 108 | 109 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 110 | 111 | ## Typing getDerivedStateFromProps 112 | 113 | Before you start using `getDerivedStateFromProps`, please go through the [documentation](https://reactjs.org/docs/react-component.html#static-getderivedstatefromprops) and [You Probably Don't Need Derived State](https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html). Derived State can be implemented using hooks which can also help set up memoization. 114 | 115 | Here are a few ways in which you can annotate `getDerivedStateFromProps` 116 | 117 | 1. If you have explicitly typed your derived state and want to make sure that the return value from `getDerivedStateFromProps` conforms to it. 118 | 119 | ```tsx 120 | class Comp extends React.Component { 121 | static getDerivedStateFromProps( 122 | props: Props, 123 | state: State 124 | ): Partial | null { 125 | // 126 | } 127 | } 128 | ``` 129 | 130 | 2. When you want the function's return value to determine your state. 131 | 132 | ```tsx 133 | class Comp extends React.Component< 134 | Props, 135 | ReturnType 136 | > { 137 | static getDerivedStateFromProps(props: Props) {} 138 | } 139 | ``` 140 | 141 | 3. When you want derived state with other state fields and memoization 142 | 143 | ```tsx 144 | type CustomValue = any; 145 | interface Props { 146 | propA: CustomValue; 147 | } 148 | interface DefinedState { 149 | otherStateField: string; 150 | } 151 | type State = DefinedState & ReturnType; 152 | function transformPropsToState(props: Props) { 153 | return { 154 | savedPropA: props.propA, // save for memoization 155 | derivedState: props.propA, 156 | }; 157 | } 158 | class Comp extends React.PureComponent { 159 | constructor(props: Props) { 160 | super(props); 161 | this.state = { 162 | otherStateField: "123", 163 | ...transformPropsToState(props), 164 | }; 165 | } 166 | static getDerivedStateFromProps(props: Props, state: State) { 167 | if (isEqual(props.propA, state.savedPropA)) return null; 168 | return transformPropsToState(props); 169 | } 170 | } 171 | ``` 172 | 173 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) 174 | -------------------------------------------------------------------------------- /docs/basic/getting-started/concurrent.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: concurrent 3 | title: Concurrent React/React Suspense 4 | --- 5 | 6 | _Not written yet._ watch for more on React Suspense and Time Slicing. 7 | 8 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 9 | -------------------------------------------------------------------------------- /docs/basic/getting-started/context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: context 3 | title: Context 4 | --- 5 | 6 | ## Basic Example 7 | 8 | ```tsx 9 | import * as React from "react"; 10 | 11 | interface AppContextInterface { 12 | name: string; 13 | author: string; 14 | url: string; 15 | } 16 | 17 | const AppCtx = React.createContext(null); 18 | 19 | // Provider in your app 20 | 21 | const sampleAppContext: AppContextInterface = { 22 | name: "Using React Context in a Typescript App", 23 | author: "thehappybug", 24 | url: "http://www.example.com", 25 | }; 26 | 27 | export const App = () => ( 28 | ... 29 | ); 30 | 31 | // Consume in your app 32 | 33 | export const PostInfo = () => { 34 | const appContext = React.useContext(AppCtx); 35 | return ( 36 |
37 | Name: {appContext.name}, Author: {appContext.author}, Url:{" "} 38 | {appContext.url} 39 |
40 | ); 41 | }; 42 | ``` 43 | 44 | You can also use the [Class.contextType](https://reactjs.org/docs/context.html#classcontexttype) or [Context.Consumer](https://reactjs.org/docs/context.html#contextconsumer) API, let us know if you have trouble with that. 45 | 46 | _[Thanks to @AlvSovereign](https://github.com/typescript-cheatsheets/react/issues/97)_ 47 | 48 | ## Extended Example 49 | 50 | Using `React.createContext` with an empty object as default value. 51 | 52 | ```tsx 53 | interface ContextState { 54 | // set the type of state you want to handle with context e.g. 55 | name: string | null; 56 | } 57 | //set an empty object as default state 58 | const Context = React.createContext({} as ContextState); 59 | // set up context provider as you normally would in JavaScript [React Context API](https://reactjs.org/docs/context.html#api) 60 | ``` 61 | 62 | Using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with **no `defaultValue`, yet no need to check for `undefined`**: 63 | 64 | ```ts 65 | import * as React from "react"; 66 | 67 | const currentUserContext = React.createContext(undefined); 68 | 69 | function EnthusasticGreeting() { 70 | const currentUser = React.useContext(currentUserContext); 71 | return
HELLO {currentUser!.toUpperCase()}!
; 72 | } 73 | 74 | function App() { 75 | return ( 76 | 77 | 78 | 79 | ); 80 | } 81 | ``` 82 | 83 | Notice the explicit type arguments which we need because we don't have a default `string` value: 84 | 85 | ```ts 86 | const currentUserContext = React.createContext(undefined); 87 | // ^^^^^^^^^^^^^^^^^^ 88 | ``` 89 | 90 | along with the non-null assertion to tell TypeScript that `currentUser` is definitely going to be there: 91 | 92 | ```ts 93 | return
HELLO {currentUser!.toUpperCase()}!
; 94 | // ^ 95 | ``` 96 | 97 | This is unfortunate because _we know_ that later in our app, a `Provider` is going to fill in the context. 98 | 99 | There are a few solutions for this: 100 | 101 | 1. You can get around this by asserting non null: 102 | 103 | ```ts 104 | const currentUserContext = React.createContext(undefined!); 105 | ``` 106 | 107 | ([Playground here](https://www.typescriptlang.org/play/index.html?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUNBhgXABxSixgwxNHOABvOjg4JlZ2Lh5+QSg4WWw8RQCsdXEpE05uLF4BIWLNZ0S4ShguZjgtC2AANyMACS8AGX6AeXjyjOqoBRgIPjAwGrQsGIBfey0Aeg7u+mW6V2Z3TwBBOZj4hqaWtrHKzJqxTQUABWJO4CtszuQAGw4saTIAGVfMgAO7MMhGBpJLQ+GD+QJsELhLCRfQGODrKEw9Y3KpZWpSZ6vd5CIw7IA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error. 108 | 109 | 2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: 110 | 111 | ```tsx 112 | import * as React from "react"; 113 | 114 | /** 115 | * A helper to create a Context and Provider with no upfront default value, and 116 | * without having to check for undefined all the time. 117 | */ 118 | function createCtx() { 119 | const ctx = React.createContext(undefined); 120 | function useCtx() { 121 | const c = React.useContext(ctx); 122 | if (c === undefined) 123 | throw new Error("useCtx must be inside a Provider with a value"); 124 | return c; 125 | } 126 | return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple 127 | } 128 | 129 | // Usage: 130 | 131 | // We still have to specify a type, but no default! 132 | export const [useCurrentUserName, CurrentUserProvider] = createCtx(); 133 | 134 | function EnthusasticGreeting() { 135 | const currentUser = useCurrentUserName(); 136 | return
HELLO {currentUser.toUpperCase()}!
; 137 | } 138 | 139 | function App() { 140 | return ( 141 | 142 | 143 | 144 | ); 145 | } 146 | ``` 147 | 148 | [View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) 149 | 150 | 3. You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). 151 | 152 | ```tsx 153 | /** 154 | * A helper to create a Context and Provider with no upfront default value, and 155 | * without having to check for undefined all the time. 156 | */ 157 | function createCtx
() { 158 | const ctx = React.createContext(undefined); 159 | function useCtx() { 160 | const c = React.useContext(ctx); 161 | if (c === undefined) 162 | throw new Error("useCtx must be inside a Provider with a value"); 163 | return c; 164 | } 165 | return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple 166 | } 167 | 168 | // usage 169 | 170 | export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! 171 | export function App() { 172 | const key = useCustomHook("key"); // get a value from a hook, must be in a component 173 | return ( 174 | 175 | 176 | 177 | ); 178 | } 179 | export function Component() { 180 | const key = useCtx(); // can still use without null check! 181 | return
{key}
; 182 | } 183 | ``` 184 | 185 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) 186 | 187 | 4. Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: 188 | 189 | ```tsx 190 | export function createCtx
(defaultValue: A) { 191 | type UpdateType = React.Dispatch< 192 | React.SetStateAction 193 | >; 194 | const defaultUpdate: UpdateType = () => defaultValue; 195 | const ctx = React.createContext({ 196 | state: defaultValue, 197 | update: defaultUpdate, 198 | }); 199 | function Provider(props: React.PropsWithChildren<{}>) { 200 | const [state, update] = React.useState(defaultValue); 201 | return ; 202 | } 203 | return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] 204 | } 205 | 206 | // usage 207 | 208 | const [ctx, TextProvider] = createCtx("someText"); 209 | export const TextContext = ctx; 210 | export function App() { 211 | return ( 212 | 213 | 214 | 215 | ); 216 | } 217 | export function Component() { 218 | const { state, update } = React.useContext(TextContext); 219 | return ( 220 | 224 | ); 225 | } 226 | ``` 227 | 228 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) 229 | 230 | 5. A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. 231 | 232 |
233 | 234 | Mutable Context Using a Class component wrapper 235 | 236 | _Contributed by: [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/13)_ 237 | 238 | ```tsx 239 | interface ProviderState { 240 | themeColor: string; 241 | } 242 | 243 | interface UpdateStateArg { 244 | key: keyof ProviderState; 245 | value: string; 246 | } 247 | 248 | interface ProviderStore { 249 | state: ProviderState; 250 | update: (arg: UpdateStateArg) => void; 251 | } 252 | 253 | const Context = React.createContext({} as ProviderStore); // type assertion on empty object 254 | 255 | class Provider extends React.Component<{}, ProviderState> { 256 | public readonly state = { 257 | themeColor: "red", 258 | }; 259 | 260 | private update = ({ key, value }: UpdateStateArg) => { 261 | this.setState({ [key]: value }); 262 | }; 263 | 264 | public render() { 265 | const store: ProviderStore = { 266 | state: this.state, 267 | update: this.update, 268 | }; 269 | 270 | return ( 271 | {this.props.children} 272 | ); 273 | } 274 | } 275 | 276 | const Consumer = Context.Consumer; 277 | ``` 278 | 279 |
280 | 281 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 282 | -------------------------------------------------------------------------------- /docs/basic/getting-started/default-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: default_props 3 | title: Typing defaultProps 4 | --- 5 | 6 | ## You May Not Need `defaultProps` 7 | 8 | As per [this tweet](https://twitter.com/dan_abramov/status/1133878326358171650), defaultProps will eventually be deprecated. You can check the discussions here: 9 | 10 | - https://twitter.com/hswolff/status/1133759319571345408 11 | 12 | The consensus is to use object default values. 13 | 14 | Function Components: 15 | 16 | ```tsx 17 | type GreetProps = { age?: number }; 18 | 19 | const Greet = ({ age = 21 }: GreetProps) => // etc 20 | ``` 21 | 22 | Class Components: 23 | 24 | ```tsx 25 | type GreetProps = { 26 | age?: number; 27 | }; 28 | 29 | class Greet extends React.Component { 30 | render() { 31 | const { age = 21 } = this.props; 32 | /*...*/ 33 | } 34 | } 35 | 36 | let el = ; 37 | ``` 38 | 39 | ## Typing `defaultProps` 40 | 41 | Type inference improved greatly for `defaultProps` in [TypeScript 3.0+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html), although [some edge cases are still problematic](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/61). 42 | 43 | **Function Components** 44 | 45 | ```tsx 46 | // using typeof as a shortcut; note that it hoists! 47 | // you can also declare the type of DefaultProps if you choose 48 | // e.g. https://github.com/typescript-cheatsheets/react/issues/415#issuecomment-841223219 49 | type GreetProps = { age: number } & typeof defaultProps; 50 | 51 | const defaultProps = { 52 | age: 21, 53 | }; 54 | 55 | const Greet = (props: GreetProps) => { 56 | // etc 57 | }; 58 | Greet.defaultProps = defaultProps; 59 | ``` 60 | 61 | _[See this in TS Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAOKVYwAKxY6ALxwA3igDmWAFxwAdgFcQAIyxQ4AXzgAyOM1YQCcACZYCyeQBte-VPVwRZqeCbOXrEAXGEi6cCdLgAJgBGABo6dXo6e0d4TixuLzgACjAbGXjuPg9UAEovAD5RXzhKGHkoWTgAHiNgADcCkTScgDpkSTgAeiQFZVVELvVqrrrGiPpMmFaXcytsz2FZtwXbOiA)_ 62 | 63 | For **Class components**, there are [a couple ways to do it](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/103#issuecomment-481061483) (including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: 64 | 65 | ```tsx 66 | type GreetProps = typeof Greet.defaultProps & { 67 | age: number; 68 | }; 69 | 70 | class Greet extends React.Component { 71 | static defaultProps = { 72 | age: 21, 73 | }; 74 | /*...*/ 75 | } 76 | 77 | // Type-checks! No type assertions needed! 78 | let el = ; 79 | ``` 80 | 81 |
82 | 83 | 84 | `JSX.LibraryManagedAttributes` nuance for library authors 85 | 86 | 87 | 88 | The above implementations work fine for App creators, but sometimes you want to be able to export `GreetProps` so that others can consume it. The problem here is that the way `GreetProps` is defined, `age` is a required prop when it isn't because of `defaultProps`. 89 | 90 | The insight to have here is that [`GreetProps` is the _internal_ contract for your component, not the _external_, consumer facing contract](https://github.com/typescript-cheatsheets/react/issues/66#issuecomment-453878710). You could create a separate type specifically for export, or you could make use of the `JSX.LibraryManagedAttributes` utility: 91 | 92 | ```tsx 93 | // internal contract, should not be exported out 94 | type GreetProps = { 95 | age: number; 96 | }; 97 | 98 | class Greet extends Component { 99 | static defaultProps = { age: 21 }; 100 | } 101 | 102 | // external contract 103 | export type ApparentGreetProps = JSX.LibraryManagedAttributes< 104 | typeof Greet, 105 | GreetProps 106 | >; 107 | ``` 108 | 109 | This will work properly, although hovering over`ApparentGreetProps`may be a little intimidating. You can reduce this boilerplate with the`ComponentProps` utility detailed below. 110 | 111 |
112 | 113 | ## Consuming Props of a Component with defaultProps 114 | 115 | A component with `defaultProps` may seem to have some required props that actually aren't. 116 | 117 | ### Problem Statement 118 | 119 | Here's what you want to do: 120 | 121 | ```tsx 122 | interface IProps { 123 | name: string; 124 | } 125 | const defaultProps = { 126 | age: 25, 127 | }; 128 | const GreetComponent = ({ name, age }: IProps & typeof defaultProps) => ( 129 |
{`Hello, my name is ${name}, ${age}`}
130 | ); 131 | GreetComponent.defaultProps = defaultProps; 132 | 133 | const TestComponent = (props: React.ComponentProps) => { 134 | return

; 135 | }; 136 | 137 | // Property 'age' is missing in type '{ name: string; }' but required in type '{ age: number; }' 138 | const el = ; 139 | ``` 140 | 141 | ### Solution 142 | 143 | Define a utility that applies `JSX.LibraryManagedAttributes`: 144 | 145 | ```tsx 146 | type ComponentProps = T extends 147 | | React.ComponentType 148 | | React.Component 149 | ? JSX.LibraryManagedAttributes 150 | : never; 151 | 152 | const TestComponent = (props: ComponentProps) => { 153 | return

; 154 | }; 155 | 156 | // No error 157 | const el = ; 158 | ``` 159 | 160 | [_See this in TS Playground_](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAMImQB2W3MABWJhUAHgAqAPjgBeOOLhYAHjD4ATdNjwwAdJ3ARe-cSyyjg3AlihwB0gD6Yqu-Tz4xzl67cl04cAH44ACkAZQANHQAZYAAjKGQoJgBZZG5kAHMsNQBBGBgoOIBXVTFxABofPzgALjheADdrejoLVSgCPDYASSEIETgAb2r0kCw61AKLDPoAXzpcQ0m4NSxOooAbQWF0OWH-TPG4ACYAVnK6WfpF7mWAcUosGFdDd1k4AApB+uQxysO4LM6r0dnAAGRwZisCAEFZrZCbbb9VAASlk0g+1VEamADUkgwABgAJLAbDYQSogJg-MZwYDoAAkg1GWFmlSZh1mBNmogA9Di8XQUfQHlgni8jLpVustn0BnJpQjZTsWrzeXANsh2gwbstxFhJhK3nIPmAdnUjfw5WIoVgYXBReKuK9+JI0TJpPs4JQYEUoNw4KIABYARjgvN8VwYargADkIIooMQoAslvBSe8JAbns7JTSsDIyAQIBAyOHJDQgA) 161 | 162 | ## Misc Discussions and Knowledge 163 | 164 |
165 | Why does React.FC break defaultProps? 166 | 167 | You can check the discussions here: 168 | 169 | - https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 170 | - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 171 | - https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87 172 | 173 | This is just the current state and may be fixed in future. 174 | 175 |
176 | 177 |
178 | TypeScript 2.9 and earlier 179 | 180 | For TypeScript 2.9 and earlier, there's more than one way to do it, but this is the best advice we've yet seen: 181 | 182 | ```ts 183 | type Props = Required & { 184 | /* additional props here */ 185 | }; 186 | 187 | export class MyComponent extends React.Component { 188 | static defaultProps = { 189 | foo: "foo", 190 | }; 191 | } 192 | ``` 193 | 194 | Our former recommendation used the `Partial type` feature in TypeScript, which means that the current interface will fulfill a partial version on the wrapped interface. In that way we can extend defaultProps without any changes in the types! 195 | 196 | ```ts 197 | interface IMyComponentProps { 198 | firstProp?: string; 199 | secondProp: IPerson[]; 200 | } 201 | 202 | export class MyComponent extends React.Component { 203 | public static defaultProps: Partial = { 204 | firstProp: "default", 205 | }; 206 | } 207 | ``` 208 | 209 | The problem with this approach is it causes complex issues with the type inference working with `JSX.LibraryManagedAttributes`. Basically it causes the compiler to think that when creating a JSX expression with that component, that all of its props are optional. 210 | 211 | [See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57) and [here](https://github.com/typescript-cheatsheets/react/issues/61). 212 | 213 |
214 | 215 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 216 | -------------------------------------------------------------------------------- /docs/basic/getting-started/error-boundaries.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: error_boundaries 3 | title: Error Boundaries 4 | --- 5 | 6 | ### Option 1: Using react-error-boundary 7 | 8 | [React-error-boundary](https://github.com/bvaughn/react-error-boundary) - is a lightweight package ready to use for this scenario with TS support built-in. 9 | This approach also lets you avoid class components that are not that popular anymore. 10 | 11 | ### Options 2: Writing your custom error boundary component 12 | 13 | If you don't want to add a new npm package for this, you can also write your own `ErrorBoundary` component. 14 | 15 | ```jsx 16 | import React, { Component, ErrorInfo, ReactNode } from "react"; 17 | 18 | interface Props { 19 | children: ReactNode; 20 | } 21 | 22 | interface State { 23 | hasError: boolean; 24 | } 25 | 26 | class ErrorBoundary extends Component { 27 | public state: State = { 28 | hasError: false 29 | }; 30 | 31 | public static getDerivedStateFromError(_: Error): State { 32 | // Update state so the next render will show the fallback UI. 33 | return { hasError: true }; 34 | } 35 | 36 | public componentDidCatch(error: Error, errorInfo: ErrorInfo) { 37 | console.error("Uncaught error:", error, errorInfo); 38 | } 39 | 40 | public render() { 41 | if (this.state.hasError) { 42 | return

Sorry.. there was an error

; 43 | } 44 | 45 | return this.props.children; 46 | } 47 | } 48 | 49 | export default ErrorBoundary; 50 | 51 | ``` 52 | 53 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). 54 | -------------------------------------------------------------------------------- /docs/basic/getting-started/forms-and-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: forms_and_events 3 | title: Forms and Events 4 | --- 5 | 6 | If performance is not an issue (and it usually isn't!), inlining handlers is easiest as you can just use [type inference and contextual typing](https://www.typescriptlang.org/docs/handbook/type-inference.html#contextual-typing): 7 | 8 | ```tsx 9 | const el = ( 10 |