├── .nvmrc ├── .dockerignore ├── .github ├── workflows │ ├── mlc_config.json │ └── main.yml ├── ISSUE_TEMPLATE │ ├── hoc-cheatsheet.md │ ├── advanced-cheatsheet.md │ ├── migrating-cheatsheet.md │ ├── general-react-ts-question.md │ └── basic-cheatsheet.md ├── pull_request_template.md └── stale.yml ├── .husky └── pre-commit ├── website ├── static │ └── img │ │ ├── icon.png │ │ ├── favicon.ico │ │ ├── oss_logo.png │ │ └── undraw_tweetstorm.svg ├── package.json ├── README.md ├── src │ ├── pages │ │ ├── index.js │ │ ├── users.js │ │ └── help.js │ └── css │ │ └── custom.css ├── sidebars.json └── docusaurus.config.js ├── docs ├── basic │ ├── getting-started │ │ ├── concurrent.md │ │ ├── error-boundaries.md │ │ ├── context.md │ │ ├── forward-create-ref.md │ │ ├── portals.md │ │ ├── function-components.md │ │ ├── class-components.md │ │ ├── default-props.md │ │ ├── basic-type-examples.md │ │ └── forms-and-events.md │ ├── examples.md │ ├── troubleshooting │ │ ├── non-ts-files.md │ │ ├── operators.md │ │ ├── utilities.md │ │ ├── learn-ts.md │ │ ├── official-typings-bugs.md │ │ └── ts-config.md │ ├── recommended │ │ ├── talks.md │ │ ├── codebases.md │ │ └── resources.md │ ├── editor-integration.md │ ├── setup.md │ └── linting.md ├── react-types │ ├── index.md │ ├── ReactNode.md │ └── ComponentProps.md ├── migration │ ├── js-docs.md │ ├── from-flow.md │ ├── from-js.md │ └── index.md ├── advanced │ ├── utility-types.md │ ├── index.md │ ├── types-react-ap.md │ └── misc-concerns.md └── hoc │ ├── index.md │ ├── full-example.md │ └── excluding-props.md ├── copyFile.js ├── netlify.toml ├── LICENSE ├── package.json ├── CONTRIBUTING.md ├── .gitignore ├── code-of-conduct.md ├── .all-contributorsrc ├── CONTRIBUTORS.md └── genReadme.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.x 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | */node_modules 2 | *.log 3 | -------------------------------------------------------------------------------- /.github/workflows/mlc_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "aliveStatusCodes": [200, 429] 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | yarn pretty-quick --staged 5 | -------------------------------------------------------------------------------- /website/static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkhorse00512/React/HEAD/website/static/img/icon.png -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkhorse00512/React/HEAD/website/static/img/favicon.ico -------------------------------------------------------------------------------- /website/static/img/oss_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/darkhorse00512/React/HEAD/website/static/img/oss_logo.png -------------------------------------------------------------------------------- /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/issues/new). 9 | -------------------------------------------------------------------------------- /docs/react-types/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: React Types 3 | --- 4 | 5 | `@types/react` makes some types available that can be very useful. Here's a list in alphabetical order with links to the detailed reference pages. 6 | 7 | - [`ComponentProps`](/docs/react-types/ComponentProps) 8 | - [`ReactNode`](/docs/react-types/ReactNode) 9 | -------------------------------------------------------------------------------- /.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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /copyFile.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const filesTopCopy = [ 3 | { 4 | src: "../CONTRIBUTORS.md", 5 | dest: "src/pages/contributors.md", 6 | }, 7 | { 8 | src: "../CONTRIBUTING.md", 9 | dest: "src/pages/contributing.md", 10 | }, 11 | ]; 12 | 13 | function copyFile(src, dest) { 14 | fs.copyFile(src, dest, (err) => { 15 | if (err) { 16 | console.log("Error Found:", err); 17 | } else { 18 | console.log("Files copied"); 19 | } 20 | }); 21 | } 22 | 23 | filesTopCopy.forEach(({ src, dest }) => { 24 | copyFile(src, dest); 25 | }); 26 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Directory to change to before starting a build. 3 | # This is where we will look for package.json/.nvmrc/etc. 4 | base = "website" 5 | 6 | # otherwise netlify keeps ignoring changes in /docs 7 | ignore = "/bin/false" 8 | 9 | # Directory (relative to root of your repo) that contains the deploy-ready 10 | # HTML files and assets generated by the build. If a base directory has 11 | # been specified, include it in the publish directory path. 12 | publish = "build" 13 | 14 | # Default build command. 15 | command = "yarn build" 16 | 17 | # Directory with the serverless Lambda functions to deploy to AWS. 18 | # functions = "project/functions/" 19 | -------------------------------------------------------------------------------- /.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: CI 2 | on: 3 | push: 4 | branches: [main] 5 | pull_request: 6 | branches: [main] 7 | 8 | jobs: 9 | files-up-to-date: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout commit 13 | uses: actions/checkout@v4 14 | 15 | - name: Setup node 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version-file: ".nvmrc" 19 | cache: yarn 20 | 21 | - name: Install dependencies 22 | run: yarn install --frozen-lockfile 23 | 24 | - name: "`yarn format` changes committed?" 25 | run: yarn format:check 26 | 27 | - name: "`yarn gen-readme` changes committed?" 28 | run: | 29 | yarn gen-readme 30 | git diff --exit-code 31 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "add-pages-on-site": "node ../copyFile.js", 4 | "start": "yarn add-pages-on-site && docusaurus start", 5 | "build": "yarn add-pages-on-site && docusaurus build", 6 | "swizzle": "docusaurus swizzle", 7 | "deploy": "yarn add-pages-on-site && docusaurus deploy" 8 | }, 9 | "dependencies": { 10 | "@docusaurus/core": "^2.4.0", 11 | "@docusaurus/preset-classic": "^2.4.0", 12 | "classnames": "^2.3.2", 13 | "prism-react-renderer": "^2.4.0", 14 | "react": "^17.0.2", 15 | "react-dom": "^17.0.2" 16 | }, 17 | "browserslist": { 18 | "production": [ 19 | ">0.2%", 20 | "not dead", 21 | "not op_mini all" 22 | ], 23 | "development": [ 24 | "last 1 chrome version", 25 | "last 1 firefox version", 26 | "last 1 safari version" 27 | ] 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. 26 | 27 | ### Deployment 28 | 29 | ``` 30 | $ GIT_USER= USE_SSH=true yarn deploy 31 | ``` 32 | 33 | If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch. 34 | -------------------------------------------------------------------------------- /docs/migration/js-docs.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: js_docs 3 | title: JSDoc 4 | --- 5 | 6 | - https://github.com/Microsoft/TypeScript/wiki/JsDoc-support-in-JavaScript 7 | - webpack's codebase uses JSDoc with linting by TS https://twitter.com/TheLarkInn/status/984479953927327744 (some crazy hack: https://twitter.com/thelarkinn/status/996475530944823296) 8 | - JSDoc can type check if using closure-compiler https://github.com/google/closure-compiler/wiki/Types-in-the-Closure-Type-System 9 | 10 | Problems to be aware of: 11 | 12 | - `object` is converted to `any` for some reason. 13 | - If you have an error in the jsdoc, you get no warning/error. TS just silently doesn't type annotate the function. 14 | - [casting can be verbose](https://twitter.com/bahmutov/status/1089229349637754880) 15 | 16 | (_thanks [Gil Tayar](https://twitter.com/giltayar/status/1089228919260221441) and [Gleb Bahmutov](https://twitter.com/bahmutov/status/1089229196247908353) for sharing above commentary_) 17 | -------------------------------------------------------------------------------- /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 having a good working knowledge of how to construct the inbuilt utility types from scratch. See [Dr. Rauschmayer'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/troubleshooting/non-ts-files.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: non_ts_files 3 | title: "Troubleshooting Handbook: Globals, Images and other non-TS files" 4 | sidebar_label: Globals, Images and other non-TS files 5 | --- 6 | 7 | Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html). 8 | 9 | If, say, you are using a third party JS script that attaches on to the `window` global, you can extend `Window`: 10 | 11 | ```ts 12 | declare global { 13 | interface Window { 14 | MyVendorThing: MyVendorType; 15 | } 16 | } 17 | ``` 18 | 19 | Likewise if you wish to "import" an image or other non TS/TSX file: 20 | 21 | ```ts 22 | // declaration.d.ts 23 | // anywhere in your project, NOT the same name as any of your .ts/tsx files 24 | declare module "*.png"; 25 | 26 | // importing in a tsx file 27 | import * as logo from "./logo.png"; 28 | ``` 29 | 30 | Note that `tsc` cannot bundle these files for you, you will have to use Webpack or Parcel. 31 | 32 | Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 and [StackOverflow](https://stackoverflow.com/a/49715468/4216035) 33 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/react-types/ReactNode.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ReactNode 3 | --- 4 | 5 | `ReactNode` is a type that describes what React can render. 6 | 7 | ## Parameters 8 | 9 | `ReactNode` does not take any parameters. 10 | 11 | ## Usage 12 | 13 | ### Typing `children` 14 | 15 | The most common use case for `ReactNode` is typing `children`. 16 | 17 | ```tsx 18 | import { ReactNode } from "react"; 19 | 20 | interface Props { 21 | children?: ReactNode; 22 | } 23 | 24 | function Component({ children }: Props) { 25 | return children; 26 | } 27 | ``` 28 | 29 | `` accepts anything that React can render as `children`. Here are some examples: 30 | 31 | ```tsx 32 | function Examples() { 33 | return ( 34 | <> 35 | 36 |
Hello
37 |
38 | Hello 39 | {123} 40 | 41 | <>Hello 42 | 43 | {true} 44 | {null} 45 | {undefined} 46 | {[1, 2, 3]} 47 | 48 | ); 49 | } 50 | ``` 51 | -------------------------------------------------------------------------------- /docs/basic/recommended/talks.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: talks 3 | title: Recommended React + TypeScript talks 4 | sidebar_label: Talks 5 | --- 6 | 7 | - [Ultimate React Component Patterns with TypeScript](https://www.youtube.com/watch?v=_PBQ3if6Fmg), by Martin Hochel, GeeCon Prague 2018 8 | - [How to Build React Apps with TypeScript](https://youtu.be/LJzGGmu5APA?si=YNzy7T_8yj7TuXxS), by ClearEdge Tech Talk 2022 9 | - [Create a More Readable React Codebase Using TypeScript](https://youtu.be/nkJbGgieALI?si=IFZZIMEiXz7AsiBv), by Emma Brillhart 2019 10 | - [Advanced TypeScript with React](https://youtu.be/zQfD4ZxxyKA?si=FmrgOq667svX6C9O), by Nikhil Verma 2019 11 | - [Senior Typescript Features You don't Know About - clean-code](https://www.youtube.com/watch?v=Y4u97vJqmhM), by CoderOne 2023 12 | - [React & TypeScript - Course for Beginners](https://www.youtube.com/watch?v=FJDVKeh7RJI), by FreeCodeCamp 2022 13 | - [TypeScript + React](https://www.youtube.com/watch?v=1ZnrX3wiNTU), by Chris Toomey 2019 14 | - [Mastering React Hooks](https://www.youtube.com/watch?v=zM_ZiSl2n2E), by Jack Herrington 2021 15 | - [Using Hooks and codegen](https://www.youtube.com/watch?v=cdsnzfJUqm0) by Tejas Kumar 2019 16 | 17 | - Please help contribute to this new section! 18 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-typescript-cheatsheet", 3 | "version": "1.0.0", 4 | "description": "this package.json is just for maintenance work", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/typescript-cheatsheets/react.git" 8 | }, 9 | "keywords": [ 10 | "react", 11 | "typescript", 12 | "guides" 13 | ], 14 | "author": "", 15 | "license": "ISC", 16 | "bugs": { 17 | "url": "https://github.com/typescript-cheatsheets/react/issues" 18 | }, 19 | "homepage": "https://github.com/typescript-cheatsheets/react#readme", 20 | "scripts": { 21 | "format-readme": "prettier --write \"README.md\"", 22 | "gen-readme": "node genReadme.js", 23 | "format": "prettier --write \"**/*.md\"", 24 | "format:check": "prettier --check \"**/*.md\"", 25 | "postinstall": "cd website && yarn", 26 | "prepare": "husky install", 27 | "start": "yarn --cwd website start", 28 | "build": "yarn --cwd website build" 29 | }, 30 | "dependencies": { 31 | "front-matter": "^4.0.2", 32 | "markdown-toc": "^1.2.0", 33 | "yargs": "^17.5.1" 34 | }, 35 | "devDependencies": { 36 | "husky": "^7.0.0", 37 | "prettier": "^2.6.2", 38 | "pretty-quick": "^3.1.3" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /docs/basic/troubleshooting/operators.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: operators 3 | title: "Troubleshooting Handbook: Operators" 4 | sidebar_label: Operators 5 | --- 6 | 7 | - `typeof` and `instanceof`: type query used for refinement 8 | - `keyof`: get keys of an object. `keyof T` is an operator to tell you what values of `k` can be used for `obj[k]`. 9 | - [Some misconceptions here](https://twitter.com/SeaRyanC/status/1418678670739218438?s=20). 10 | - `O[K]`: property lookup 11 | - `[K in O]`: mapped types 12 | - `+` or `-` or `readonly` or `?`: addition and subtraction and readonly and optional modifiers 13 | - `x ? Y : Z`: Conditional types for generic types, type aliases, function parameter types 14 | - `!`: Nonnull assertion for nullable types 15 | - `=`: Generic type parameter default for generic types 16 | - `as`: type assertion 17 | - `is`: type guard for function return types 18 | 19 | Conditional Types are a difficult topic to get around so here are some extra resources: 20 | 21 | - fully walked through explanation https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/ 22 | - Bailing out and other advanced topics https://github.com/sw-yx/ts-spec/blob/master/conditional-types.md 23 | - Basarat's video https://www.youtube.com/watch?v=SbVgPQDealg&list=PLYvdvJlnTOjF6aJsWWAt7kZRJvzw-en8B&index=2&t=0s 24 | - [Generics, Conditional types and Mapped types](https://www.youtube.com/watch?v=PJjeHzvi_VQ&feature=youtu.be) 25 | -------------------------------------------------------------------------------- /website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Link from "@docusaurus/Link"; 3 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 4 | import useBaseUrl from "@docusaurus/useBaseUrl"; 5 | 6 | import Layout from "@theme/Layout"; 7 | 8 | export default function Home() { 9 | const { siteConfig } = useDocusaurusContext(); 10 | return ( 11 | 16 |
17 |
18 |
19 |

{siteConfig.title}

20 |

{siteConfig.tagline}

21 |
22 |
23 | 27 | Getting started 28 | 29 | 33 | Join Official Discord 34 | 35 |
36 |
37 |
38 |
39 | ); 40 | } 41 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --ifm-color-primary: #138cd3; 3 | --ifm-color-primary-dark: #117ebe; 4 | --ifm-color-primary-darker: #1077b3; 5 | --ifm-color-primary-darkest: #0c5a87; 6 | --ifm-color-primary-light: #159ae8; 7 | --ifm-color-primary-lighter: #1e9feb; 8 | --ifm-color-primary-lightest: #3dacee; 9 | } 10 | 11 | .navbar__inner { 12 | max-width: 1400px; 13 | margin: 0 auto; 14 | } 15 | 16 | .navbar__title { 17 | white-space: normal; 18 | word-break: break-word; 19 | } 20 | 21 | .footer__logo { 22 | max-width: 4rem; 23 | max-height: 4rem; 24 | } 25 | 26 | main details { 27 | margin-top: 1rem; 28 | margin-bottom: 1rem; 29 | padding: 1rem 1rem 0; 30 | border: 0.15rem solid var(--ifm-color-emphasis-300); 31 | border-radius: var(--ifm-pagination-nav-border-radius); 32 | } 33 | 34 | main details summary { 35 | margin-bottom: 1rem; 36 | outline: none; 37 | /* Make it look like a link to notify user that it's clickable */ 38 | text-decoration: underline; 39 | 40 | /* Refer to #309 */ 41 | -webkit-user-select: none; 42 | -moz-user-select: none; 43 | -ms-user-select: none; 44 | user-select: none; 45 | } 46 | 47 | main details summary:hover { 48 | cursor: pointer; 49 | /* Hide the underline on hover */ 50 | text-decoration: none; 51 | } 52 | main details:hover { 53 | border-color: var(--ifm-pagination-nav-color-hover); 54 | } 55 | 56 | .homePageBtns { 57 | display: grid; 58 | gap: 20px; 59 | } 60 | @media (min-width: 480px) { 61 | .homePageBtns { 62 | grid-template-columns: 1fr 1fr; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /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 | ### Option 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/issues/new). 54 | -------------------------------------------------------------------------------- /docs/migration/from-flow.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: from_flow 3 | title: From Flow 4 | --- 5 | 6 | - (2022) Try Stripe's [Flow to TS codemod](https://github.com/stripe-archive/flow-to-typescript-codemod): [blogpost](https://stripe.com/blog/migrating-to-typescript), [podcast discussion](https://devtools.fm/episode/33) 7 | - Try flow2ts: `npx flow2ts` - doesn't work 100% but saves some time ([see this and other tips from @braposo](https://github.com/typescript-cheatsheets/react/pull/79#issuecomment-458227322) at TravelRepublic) 8 | - Try [Airtable's codemod](https://github.com/Airtable/typescript-migration-codemod): https://medium.com/airtable-eng/the-continual-evolution-of-airtables-codebase-migrating-a-million-lines-of-code-to-typescript-612c008baf5c 9 | - [Incremental Migration to TypeScript on a Flowtype codebase][entria] at Entria 10 | - [MemSQL's Studio's migration](https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/) - blogpost with many useful tips 11 | - Quick-n-dirty [Flow to TS Codemod](https://gist.github.com/skovhus/c57367ce6ecbc3f70bb7c80f25727a11) 12 | - [Ecobee's brief experience](https://mobile.twitter.com/alanhietala/status/1104450494754377728) 13 | - [Migrating a 50K SLOC Flow + React Native app to TypeScript](https://blog.usejournal.com/migrating-a-flow-react-native-app-to-typescript-c74c7bceae7d) 14 | - [How we migrated 400K+ lines of code from Flow to TypeScript at Factorial](https://labs.factorialhr.com/posts/how-we-migrated-400k-lines-of-code-from-flow-to-typescript) 15 | [entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" 16 | -------------------------------------------------------------------------------- /website/src/pages/users.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 3 | import Layout from "@theme/Layout"; 4 | 5 | // TODO useless user showcase page ? 6 | export default function Users() { 7 | const { siteConfig } = useDocusaurusContext(); 8 | const { users, addUserUrl } = siteConfig.customFields; 9 | 10 | return ( 11 | 12 |
13 |
14 |
15 |

Who is Using This?

16 |

This project is used by many folks

17 |
18 |
19 | {users && users.length>0&& users.map((user) => ( 20 | 26 | {user.caption} 36 | 37 | ))} 38 |
39 |

Are you using this project?

40 | 44 | Add your company 45 | 46 |
47 |
48 |
49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /docs/basic/troubleshooting/utilities.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: utilities 3 | title: "Troubleshooting Handbook: Utilities" 4 | sidebar_label: Utilities 5 | --- 6 | 7 | These are all built in, [see source in es5.d.ts](https://github.com/microsoft/TypeScript/blob/33a34e5b96bfe086266f4765ab9789a2a02507f9/src/lib/es5.d.ts#L1523-L1637): 8 | 9 | - `Awaited`: emulate the behavior of `await` 10 | - `Capitalize`: convert first character of string literal type to uppercase 11 | - `ConstructorParameters`: a tuple of class constructor's parameter types 12 | - `Exclude`: exclude a type from another type 13 | - `Extract`: select a subtype that is assignable to another type 14 | - `InstanceType`: the instance type you get from a `new`ing a class constructor 15 | - `Lowercase`: convert string literal type to lowercase 16 | - `NonNullable`: exclude `null` and `undefined` from a type 17 | - `Omit`: construct a type with the properties of another type. 18 | - `OmitThisParameter`: remove the 'this' parameter from a function type. 19 | - `Parameters`: a tuple of a function's parameter types 20 | - `Partial`: Make all properties in an object optional 21 | - `Readonly`: Make all properties in an object readonly 22 | - `ReadonlyArray`: Make an immutable array of the given type 23 | - `Pick`: A subtype of an object type with a subset of its keys 24 | - `Record`: A map from a key type to a value type 25 | - `Required`: Make all properties in an object required 26 | - `ReturnType`: A function's return type 27 | - `ThisParameterType`: extract the type of the 'this' parameter of a function type 28 | - `ThisType`: marker for contextual 'this' type 29 | - `Uncapitalize`: convert first character of string literal type to lowercase 30 | - `Uppercase`: convert string literal type to uppercase 31 | -------------------------------------------------------------------------------- /website/src/pages/help.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 3 | import useBaseUrl from "@docusaurus/useBaseUrl"; 4 | import Link from "@docusaurus/Link"; 5 | import Layout from "@theme/Layout"; 6 | 7 | const SupportLink = ({ title, content }) => ( 8 |
9 |

{title}

10 |
{content}
11 |
12 | ); 13 | 14 | export default function Help() { 15 | const { siteConfig } = useDocusaurusContext(); 16 | 17 | const supportLinks = [ 18 | { 19 | title: "Browse Docs", 20 | content: ( 21 | <> 22 | Learn more using the{" "} 23 | 24 | documentation on this site 25 | 26 | . 27 | 28 | ), 29 | }, 30 | { 31 | title: "Join the community", 32 | content: "Ask questions about the documentation and project", 33 | }, 34 | { 35 | title: "Stay up to date", 36 | content: "Find out what's new with this project", 37 | }, 38 | ]; 39 | 40 | return ( 41 | 42 |
43 |
44 |
45 |

Need help?

46 |
47 |

This project is maintained by a dedicated group of people.

48 |
49 | 50 |
51 | {supportLinks.map((supportLink, i) => ( 52 |
53 | 54 |
55 | ))} 56 |
57 |
58 |
59 | ); 60 | } 61 | -------------------------------------------------------------------------------- /docs/basic/troubleshooting/learn-ts.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: learn_ts 3 | title: "Time to Really Learn TypeScript" 4 | sidebar_label: Time to Really Learn TypeScript 5 | --- 6 | 7 | Believe it or not, we have only barely introduced TypeScript here in this cheatsheet. If you are still facing TypeScript troubleshooting issues, it is likely that your understanding of TS is still too superficial. 8 | 9 | There is a whole world of generic type logic that you will eventually get into, however it becomes far less dealing with React than just getting good at TypeScript so it is out of scope here. But at least you can get productive in React now :) 10 | 11 | It is worth mentioning some resources to help you get started: 12 | 13 | - Step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section, written by @Orta 14 | - Anders Hejlsberg's overview of TS: https://www.youtube.com/watch?v=ET4kT88JRXs 15 | - Marius Schultz: https://blog.mariusschulz.com/series/typescript-evolution with an [Egghead.io course](https://egghead.io/courses/advanced-static-types-in-typescript) 16 | - Basarat's Deep Dive: https://basarat.gitbook.io/typescript/ 17 | - Axel Rauschmeyer's [Tackling TypeScript](https://exploringjs.com/tackling-ts/) 18 | - Rares Matei: [Egghead.io course](https://egghead.io/courses/practical-advanced-typescript)'s advanced TypeScript course on Egghead.io is great for newer typescript features and practical type logic applications (e.g. recursively making all properties of a type `readonly`) 19 | - Learn about [Generics, Conditional types and Mapped types](https://www.youtube.com/watch?v=PJjeHzvi_VQ&feature=youtu.be) 20 | - Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) 21 | - Here is another [TypeScript Error Guide](https://github.com/threehams/typescript-error-guide/) that you can check for your errors. 22 | -------------------------------------------------------------------------------- /docs/basic/setup.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: setup 3 | title: Setup 4 | --- 5 | 6 | ## Prerequisites 7 | 8 | You can use this cheatsheet for reference at any skill level, but basic understanding of React and TypeScript is assumed. Here is a list of prerequisites: 9 | 10 | - Basic understanding of [React](https://react.dev/). 11 | - Familiarity with [TypeScript Basics](https://www.typescriptlang.org/docs/handbook/2/basic-types.html) and [Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html). 12 | 13 | In the cheatsheet we assume you are using the latest versions of React and TypeScript. 14 | 15 | ## React and TypeScript starter kits 16 | 17 | React has documentation for [how to start a new React project](https://react.dev/learn/start-a-new-react-project) with some of the most popular frameworks. Here's how to start them with TypeScript: 18 | 19 | - [Next.js](https://nextjs.org/docs/basic-features/typescript): `npx create-next-app@latest --ts` 20 | - [Remix](https://remix.run/docs/tutorials/blog): `npx create-remix@latest` 21 | - [Gatsby](https://www.gatsbyjs.com/docs/how-to/custom-configuration/typescript/): `npm init gatsby --ts` 22 | - [Expo](https://docs.expo.dev/guides/typescript/): `npx create-expo-app -t with-typescript` 23 | 24 | ## Try React and TypeScript online 25 | 26 | There are some tools that let you run React and TypeScript online, which can be helpful for debugging or making sharable reproductions. 27 | 28 | - [TypeScript playground](https://www.typescriptlang.org/play?target=8&jsx=4#code/JYWwDg9gTgLgBAbzgVwM4FMDKMCGN0A0KGAogGZnoDG8AvnGVBCHAORTo42sDcAsAChB6AB6RYcKhAB2qeAGEIyafihwAvHAAUASg0A+RILiSZcuAG0pymEQwxFNgLobiWXPi0AGHfyECTNHRyShotXQMjAJM4ABMIKmQQdBUAOhhgGAAbdFcAAwBNJUks4CoAa3RYuAASBGsVegzk1Dy-E1pfQWM4DhhkKGltHpMAHn0RmNGwfSLkErLK6vqlRrhm9FRRgHoZybGAI2QYGBk4GXlSivUECPVDe0cVLQb4AGo4AEYdWgnomJil0WcGS+zgOyOJxkfwBOxhcC6AlogiAA) 29 | - [StackBlitz](https://stackblitz.com/fork/react-ts) 30 | - [CodeSandbox](https://ts.react.new/) 31 | -------------------------------------------------------------------------------- /website/sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": { 3 | "Basic": [ 4 | "basic/setup", 5 | { 6 | "type": "category", 7 | "label": "Getting Started", 8 | "items": [ 9 | "basic/getting-started/basic_type_example", 10 | "basic/getting-started/function_components", 11 | "basic/getting-started/hooks", 12 | "basic/getting-started/class_components", 13 | "basic/getting-started/default_props", 14 | "basic/getting-started/forms_and_events", 15 | "basic/getting-started/context", 16 | "basic/getting-started/forward_and_create_ref", 17 | "basic/getting-started/portals", 18 | "basic/getting-started/error_boundaries", 19 | "basic/getting-started/concurrent" 20 | ] 21 | }, 22 | { 23 | "type": "category", 24 | "label": "Troubleshooting Handbook", 25 | "items": [ 26 | "basic/troubleshooting/types", 27 | "basic/troubleshooting/operators", 28 | "basic/troubleshooting/utilities", 29 | "basic/troubleshooting/non_ts_files", 30 | "basic/troubleshooting/tsconfig", 31 | "basic/troubleshooting/official_typings_bugs", 32 | "basic/troubleshooting/learn_ts" 33 | ] 34 | }, 35 | { 36 | "type": "category", 37 | "label": "Recommendations", 38 | "items": [ 39 | "basic/recommended/codebases", 40 | "basic/recommended/talks", 41 | "basic/recommended/resources" 42 | ] 43 | }, 44 | "basic/editor_integration", 45 | "basic/linting", 46 | "basic/examples" 47 | ], 48 | "HOC": [ 49 | "hoc/intro", 50 | "hoc/full_example", 51 | "hoc/react_hoc_docs", 52 | "hoc/excluding_props" 53 | ], 54 | "Advanced": [ 55 | "advanced/intro", 56 | "advanced/utility_types", 57 | "advanced/patterns_by_usecase", 58 | "advanced/patterns_by_version", 59 | "advanced/misc_concerns", 60 | "advanced/types_react_api" 61 | ], 62 | "Migration": [ 63 | "migration/intro", 64 | "migration/js_docs", 65 | "migration/from_js", 66 | "migration/from_flow" 67 | ] 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /docs/basic/troubleshooting/official-typings-bugs.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: official_typings_bugs 3 | title: Fixing bugs in official typings 4 | sidebar_label: Fixing bugs in official typings 5 | --- 6 | 7 | If you run into bugs with your library's official typings, you can copy them locally and tell TypeScript to use your local version using the "paths" field. In your `tsconfig.json`: 8 | 9 | ```json 10 | { 11 | "compilerOptions": { 12 | "paths": { 13 | "mobx-react": ["../typings/modules/mobx-react"] 14 | } 15 | } 16 | } 17 | ``` 18 | 19 | [Thanks to @adamrackis for the tip.](https://twitter.com/AdamRackis/status/1024827730452520963) 20 | 21 | If you just need to add an interface, or add missing members to an existing interface, you don't need to copy the whole typing package. Instead, you can use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): 22 | 23 | ```tsx 24 | // my-typings.ts 25 | declare module "plotly.js" { 26 | interface PlotlyHTMLElement { 27 | removeAllListeners(): void; 28 | } 29 | } 30 | 31 | // MyComponent.tsx 32 | import { PlotlyHTMLElement } from "plotly.js"; 33 | 34 | const f = (e: PlotlyHTMLElement) => { 35 | e.removeAllListeners(); 36 | }; 37 | ``` 38 | 39 | You don't always have to implement the module, you can simply import the module as `any` for a quick start: 40 | 41 | ```tsx 42 | // my-typings.ts 43 | declare module "plotly.js"; // each of its imports are `any` 44 | ``` 45 | 46 | Because you don't have to explicitly import this, this is known as an [ambient module declaration](https://www.typescriptlang.org/docs/handbook/namespaces-and-modules.html#pitfalls-of-namespaces-and-modules). You can do AMD's in a script-mode `.ts` file (no imports or exports), or a `.d.ts` file anywhere in your project. 47 | 48 | You can also do ambient variable and ambient type declarations: 49 | 50 | ```ts 51 | // ambient utility type 52 | type ToArray = T extends unknown[] ? T : T[]; 53 | // ambient variable 54 | declare let process: { 55 | env: { 56 | NODE_ENV: "development" | "production"; 57 | }; 58 | }; 59 | process = { 60 | env: { 61 | NODE_ENV: "production", 62 | }, 63 | }; 64 | ``` 65 | 66 | You can see examples of these included in the built in type declarations in the `lib` field of `tsconfig.json` 67 | -------------------------------------------------------------------------------- /docs/react-types/ComponentProps.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ComponentProps 3 | --- 4 | 5 | `ComponentProps` constructs a type with all valid props of an element or inferred props of a component. 6 | 7 | :::note 8 | 9 | Prefer `ComponentPropsWithRef` if ref is forwarded and `ComponentPropsWithoutRef` when ref is not forwarded. 10 | 11 | ::: 12 | 13 | ## Parameters 14 | 15 | - `ElementType`: An element type. Examples include: 16 | - An HTML or SVG element string literal such as `"div"`, `"h1"` or `"path"`. 17 | - A component type, such as `typeof Component`. 18 | 19 | ## Usage 20 | 21 | ### Get all valid props of an element 22 | 23 | `ComponentProps` can be used to create a type that includes all valid `div` props. 24 | 25 | ```tsx 26 | interface Props extends ComponentProps<"div"> { 27 | text: string; 28 | } 29 | 30 | function Component({ className, children, text, ...props }: Props) { 31 | // `props` includes `text` in addition to all valid `div` props 32 | } 33 | ``` 34 | 35 | ### Infer component props type 36 | 37 | In some cases, you might want to infer the type of a component's props. 38 | 39 | ```tsx 40 | interface Props { 41 | text: string; 42 | } 43 | 44 | function Component(props: Props) { 45 | // ... 46 | } 47 | 48 | type MyType = ComponentProps; 49 | // ^? type MyType = Props 50 | ``` 51 | 52 | #### Infer specific prop type 53 | 54 | The type of a specific prop can also be inferred this way. Let's say you are using an `` component from a component library. The component takes a `name` prop that determines what icon is shown. You need to use the type of `name` in your app, but it's not made available by the library. You could create a custom type: 55 | 56 | ```tsx 57 | type IconName = "warning" | "checkmark"; 58 | ``` 59 | 60 | However, this type is not really reflecting the actual set of icons made available by the library. A better solution is to infer the type: 61 | 62 | ```tsx 63 | import { Icon } from "component-library"; 64 | 65 | type IconName = ComponentProps["name"]; 66 | // ^? type IconName = "warning" | "checkmark" 67 | ``` 68 | 69 | You can also use the `Pick` utility type to accomplish the same thing: 70 | 71 | ```tsx 72 | import { Icon } from "component-library"; 73 | 74 | type IconName = Pick, "name">; 75 | // ^? type IconName = "warning" | "checkmark" 76 | ``` 77 | -------------------------------------------------------------------------------- /docs/hoc/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: intro 3 | sidebar_label: Intro 4 | title: HOC Cheatsheet 5 | --- 6 | 7 | **This HOC Cheatsheet** compiles all available knowledge for writing Higher Order Components with React and TypeScript. 8 | 9 | - We will map closely to [the official docs on HOCs](https://reactjs.org/docs/higher-order-components.html) initially 10 | - While hooks exist, many libraries and codebases still have a need to type HOCs. 11 | - Render props may be considered in the future 12 | - The goal is to write HOCs that offer type safety while not getting in the way. 13 | 14 | There are a lot of use cases where an HOC is used. For example: 15 | 16 | - HOCs can wrap components to check if a user is authenticated before rendering, or to restrict access based on user roles. 17 | - An HOC can conditionally render components based on feature flags or A/B testing. 18 | - An HOC can provide translation functionality to components. 19 | - An HOC can add logging or analytics tracking to components without modifying their core logic. 20 | 21 | Here is a base HOC example you can copy right away: 22 | 23 | ```jsx 24 | 25 | type PropsAreEqual

= ( 26 | prevProps: Readonly

, 27 | nextProps: Readonly

28 | ) => boolean; 29 | 30 | const withSampleHoC =

( 31 | component: { 32 | (props: P): Exclude; 33 | displayName?: string; 34 | }, 35 | propsAreEqual?: PropsAreEqual

| false, 36 | 37 | componentName = component.displayName ?? component.name 38 | ): { 39 | (props: P): React.JSX.Element; 40 | displayName: string; 41 | } => { 42 | 43 | function WithSampleHoc(props: P) { 44 | //Do something special to justify the HoC. 45 | return component(props) as React.JSX.Element; 46 | } 47 | 48 | WithSampleHoc.displayName = `withSampleHoC(${componentName})`; 49 | 50 | let wrappedComponent = propsAreEqual === false ? WithSampleHoc : React.memo(WithSampleHoc, propsAreEqual); 51 | 52 | //copyStaticProperties(component, wrappedComponent); 53 | 54 | return wrappedComponent as typeof WithSampleHoc 55 | }; 56 | ``` 57 | 58 | This code meets these criteria: 59 | 60 | 1. Allows a component to return valid elements (`strings | array | boolean | null | number`) and not just `React.JSX.Element | null`. 61 | 2. Wraps it in a memo unless you opt out. 62 | 3. Removes the nested component, so React Dev tools will just show one component. 63 | 4. Indicates with `displayName` in React Dev Tool with an annotation that this is a component wrapped in two HoCs 64 | 5. Optional: Copies over static properties that might have been defined on the original component. 65 | -------------------------------------------------------------------------------- /docs/basic/troubleshooting/ts-config.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: tsconfig 3 | title: "Troubleshooting Handbook: tsconfig.json" 4 | sidebar_label: tsconfig.json 5 | --- 6 | 7 | You can find [all the Compiler options in the TypeScript docs](https://www.typescriptlang.org/docs/handbook/compiler-options.html). [The new TS docs also has per-flag annotations of what each does](https://www.typescriptlang.org/tsconfig#allowSyntheticDefaultImports). This is the setup I roll with for APPS (not libraries - for libraries you may wish to see the settings we use in `tsdx`): 8 | 9 | ```json 10 | { 11 | "compilerOptions": { 12 | "incremental": true, 13 | "outDir": "build/lib", 14 | "target": "es5", 15 | "module": "esnext", 16 | "lib": ["DOM", "ESNext"], 17 | "sourceMap": true, 18 | "importHelpers": true, 19 | "declaration": true, 20 | "rootDir": "src", 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noImplicitReturns": true, 25 | "noFallthroughCasesInSwitch": true, 26 | "allowJs": false, 27 | "jsx": "react", 28 | "moduleResolution": "node", 29 | "baseUrl": "src", 30 | "forceConsistentCasingInFileNames": true, 31 | "esModuleInterop": true, 32 | "suppressImplicitAnyIndexErrors": true, 33 | "allowSyntheticDefaultImports": true, 34 | "experimentalDecorators": true 35 | }, 36 | "include": ["src/**/*"], 37 | "exclude": ["node_modules", "build", "scripts"] 38 | } 39 | ``` 40 | 41 | You can find more [recommended TS config here](https://github.com/tsconfig/bases). 42 | 43 | Please open an issue and discuss if there are better recommended choices for React. 44 | 45 | Selected flags and why we like them: 46 | 47 | - `esModuleInterop`: disables namespace imports (`import * as foo from "foo"`) and enables CJS/AMD/UMD style imports (`import fs from "fs"`) 48 | - `strict`: `strictPropertyInitialization` forces you to initialize class properties or explicitly declare that they can be undefined. You can opt out of this with a definite assignment assertion. 49 | - `"typeRoots": ["./typings", "./node_modules/@types"]`: By default, TypeScript looks in `node_modules/@types` and parent folders for third party type declarations. You may wish to override this default resolution so you can put all your global type declarations in a special `typings` folder. 50 | 51 | Compilation time grows linearly with size of codebase. For large projects, you will want to use [Project References](https://www.typescriptlang.org/docs/handbook/project-references.html). See our [ADVANCED](https://react-typescript-cheatsheet.netlify.app/docs/advanced) cheatsheet for commentary. 52 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Let's Contribute Together! 🚀 2 | 3 | We appreciate your interest in contributing to this project. Here are some core principles and a simplified project structure to make your contribution process more efficient and effective: 4 | 5 | ## 📋 Core Principles 6 | 7 | 1. **We're All About Cheatsheets**: Our main goal is to provide concise and easy-to-use cheatsheets. All code examples should be simple, easily searchable, and ready for copy-and-paste. 8 | 9 | 2. **Collapsible Explanations**: Keep explanations short and sweet, limited to 1-2 sentences. For more in-depth explanations, use `details` tags to provide additional context. 10 | 11 | 3. **React + TypeScript Only**: We focus on React and TypeScript. React's ecosystem is vast, so we won't cover everything. If you think there's a need, consider maintaining separate lists for specialized topics, like React + Apollo GraphQL. We also don't aim to convince people to use TypeScript; we're here to assist those who have already chosen to use it. 12 | 13 | 4. **Add TypeScript Playground Links**: For code examples longer than four lines, include a link to the TypeScript Playground. Use the default TypeScript Playground options for the best experience. 14 | 15 | Your contributions will help countless developers, including your future self! 🙌 16 | 17 | ## 📁 Project Structure 18 | 19 | - **Content in `/docs`**: All the content resides here. 20 | - The content in `/docs/basic` is compiled into `README.md` to ensure that it's easy to read on GitHub. 21 | - **`/website` Folder**: This part consumes the content in `/docs`. It's a Docusaurus 2 site and includes Algolia search. A big thanks to both the Docusaurus and Algolia teams for their support! 22 | 23 | The website is deployed on Netlify, under swyx's personal account. 24 | 25 | To run the docsite locally, follow these steps: 26 | 27 | ```bash 28 | yarn # Install dependencies 29 | ## Ensure dependencies are also installed in /website 30 | cd website && yarn start 31 | ``` 32 | 33 | Here's an example of the expected output when the development server starts successfully: 34 | 35 | ``` 36 | yarn run v1.22.4 37 | warning package.json: No license field 38 | $ docusaurus start 39 | Starting the development server... 40 | 41 | ✔ Client 42 | Compiled successfully in 9.61s 43 | 44 | ℹ 「wds」: Project is running at http://localhost:3000/ 45 | ℹ 「wds」: webpack output is served from / 46 | ℹ 「wds」: Content not from webpack is served from /Users/wanshawn/Work/react-typescript-cheatsheet/website 47 | ℹ 「wds」: 404s will fallback to /index.html 48 | 49 | ✔ Client 50 | Compiled successfully in 116.41ms 51 | ``` 52 | 53 | Let's work together to enhance this resource and make it even more valuable to the developer community! 🌟👩‍💻👨‍💻 54 | -------------------------------------------------------------------------------- /.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 | .yarn/cache 115 | .yarn/unplugged 116 | .yarn/build-state.yml 117 | .pnp.* 118 | 119 | # Miscellaneous 120 | .history 121 | .DS_Store 122 | 123 | # Docusaurus files 124 | website/build 125 | website/.docusaurus 126 | website/.cache-loader 127 | 128 | # copy of contributors for the website 129 | website/src/pages/contributing.md 130 | website/src/pages/contributors.md 131 | 132 | # Local Netlify folder 133 | .netlify 134 | 135 | # Lock-files 136 | package-lock.json 137 | yarn.lock 138 | -------------------------------------------------------------------------------- /docs/basic/recommended/codebases.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: codebases 3 | title: Recommended React + TypeScript codebases to learn from 4 | sidebar_label: Codebases 5 | --- 6 | 7 | - Apps 8 | - https://github.com/devhubapp/devhub 9 | - https://github.com/benawad/codeponder (with [coding livestream!](https://www.youtube.com/watch?v=D8IJOwdNSkc&list=PLN3n1USn4xlnI6kwzI8WrNgSdG4Z6daCq)) 10 | - https://github.com/cypress-io/cypress-realworld-app 11 | - https://github.com/alan2207/bulletproof-react 12 | - Design Systems/Component Libraries 13 | - https://github.com/seek-oss/braid-design-system/ (see [how to use TS to validate props](https://twitter.com/markdalgleish/status/1339863859469955072?s=20)) 14 | - https://github.com/palantir/blueprint 15 | - https://github.com/Shopify/polaris 16 | - https://github.com/microsoft/fluentui 17 | - Libraries 18 | - https://github.com/formium/formik/ 19 | - https://github.com/jaredpalmer/react-fns 20 | - Misc 21 | - https://github.com/NullVoxPopuli/react-vs-ember/tree/master/testing/react 22 | - https://github.com/artsy/reaction 23 | - https://github.com/artsy/emission (React Native) 24 | - [@reach/ui's community typings](https://github.com/reach/reach-ui/pull/105) 25 | - https://github.com/pshrmn/curi/tree/master/packages/router 26 | 27 | Older but still worth checking: 28 | 29 | - https://bitbucket.org/atlassian/atlaskit-mk-2/src/master/ 30 | - https://github.com/contiamo/operational-ui 31 | 32 | React Boilerplates: 33 | 34 | - https://github.com/rwieruch/nextjs-firebase-authentication: Next.js + Firebase Starter: styled, tested, typed, and authenticated 35 | - [@jpavon/react-scripts-ts](https://github.com/jpavon/react-scripts-ts) alternative react-scripts with all TypeScript features using [ts-loader](https://github.com/TypeStrong/ts-loader) 36 | - [webpack config tool](https://webpack.jakoblind.no/) is a visual tool for creating webpack projects with React and TypeScript 37 | - ready to go template with [Material-UI](https://material-ui.com/), routing and Redux 38 | 39 | React Native Boilerplates: _contributed by [@spoeck](https://github.com/typescript-cheatsheets/react/pull/20)_ 40 | 41 | - https://github.com/GeekyAnts/react-native-seed 42 | - https://github.com/lopezjurip/ReactNativeTS 43 | - https://github.com/emin93/react-native-template-typescript 44 | - 45 | 46 | TS Library Codebases to study 47 | 48 | - https://github.com/Azure/azure-sdk-for-js 49 | - https://github.com/sindresorhus/is 50 | - https://github.com/probot/probot 51 | - https://github.com/intuit/auto 52 | - https://github.com/polymer/tools 53 | - https://github.com/nteract/nteract 54 | - https://github.com/pgilad/leasot 55 | - https://github.com/JasonEtco/actions-toolkit 56 | - https://github.com/ferdaber/typescript-bootstrap/ 57 | - https://github.com/contiamo/operational-scripts 58 | - https://github.com/nobrainr/morphism 59 | - https://github.com/slackapi/node-slack-sdk 60 | -------------------------------------------------------------------------------- /docs/basic/recommended/resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: resources 3 | title: Other React + TypeScript resources 4 | sidebar_label: Other resources 5 | --- 6 | 7 | - me! 8 | - https://www.freecodecamp.org/news/how-to-build-a-todo-app-with-react-typescript-nodejs-and-mongodb/ 9 | - - **HIGHLY HIGHLY RECOMMENDED**, i wrote this repo before knowing about this one, this has a lot of stuff I don't cover, including **REDUX** and **JEST**. 10 | - [10 Bad TypeScript Habits](https://startup-cto.net/10-bad-typescript-habits-to-break-this-year/): 11 | 1. not using `"strict": true` 12 | 2. using `||` for default values when we have `??` 13 | 3. Using `any` instead of `unknown` for API responses 14 | 4. using `as` assertion instead of Type Guards (`function isFoo(obj: unknown): obj is Foo {}`) 15 | 5. `as any` in tests 16 | 6. Marking optional properties instead of modeling which combinations exist by extending interfaces 17 | 7. One letter generics 18 | 8. Non-boolean `if (nonboolean)` checks 19 | 9. bangbang checks `if (!!nonboolean)` 20 | 10. `!= null` to check for `null` and `undefined` 21 | - [Ultimate React Component Patterns with TypeScript 2.8](https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935) 22 | - [Basarat's TypeScript gitbook has a React section](https://basarat.gitbook.io/typescript/tsx/react) with an [Egghead.io course](https://egghead.io/courses/use-typescript-to-develop-react-applications) as well. 23 | - [Palmer Group's TypeScript + React Guidelines](https://github.com/palmerhq/typescript) as well as Jared's other work like [disco.chat](https://github.com/jaredpalmer/disco.chat) 24 | - [Sindre Sorhus' TypeScript Style Guide](https://github.com/sindresorhus/typescript-definition-style-guide) 25 | - [TypeScript React Starter Template by Microsoft](https://github.com/Microsoft/TypeScript-React-Starter) A starter template for TypeScript and React with a detailed README describing how to use the two together. Note: this doesn't seem to be frequently updated anymore. 26 | - [Steve Kinney's React and TypeScript course on Frontend Masters (paid)](https://frontendmasters.com/courses/react-typescript/) 27 | - [Brian Holt's Intermediate React course on Frontend Masters (paid)](https://frontendmasters.com/courses/intermediate-react/converting-the-app-to-typescript/) - Converting App To TypeScript Section 28 | - [Mike North's Production TypeScript course on Frontend Masters (paid)](https://frontendmasters.com/courses/production-typescript/) 29 | - [TSX Guide](https://jenil.github.io/chota/) by [gojutin](https://github.com/gojutin/www.tsx.guide) 30 | - TypeScript conversion: 31 | - [Lyft's React-To-TypeScript conversion CLI](https://github.com/lyft/react-javascript-to-typescript-transform) 32 | - [Gustav Wengel's blogpost - converting a React codebase to TypeScript](http://www.gustavwengel.dk/converting-typescript-to-javascript-part-1) 33 | - [Microsoft React TypeScript conversion guide](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide) 34 | - [Matt Pocock's Beginner's Typescript Tutorial](https://github.com/total-typescript/beginners-typescript-tutorial) 35 | - [Matt Pocock's React with TypeScript Tutorial](https://www.totaltypescript.com/tutorials/react-with-typescript) 36 | - [You?](https://github.com/typescript-cheatsheets/react/issues/new). 37 | -------------------------------------------------------------------------------- /docs/basic/linting.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: linting 3 | title: Linting 4 | --- 5 | 6 | > ⚠️Note that [TSLint is now in maintenance and you should try to use ESLint instead](https://medium.com/palantir/tslint-in-2019-1a144c2317a9). If you are interested in TSLint tips, please check this PR from [@azdanov](https://github.com/typescript-cheatsheets/react/pull/14). The rest of this section just focuses on ESLint. [You can convert TSlint to ESlint with this tool](https://github.com/typescript-eslint/tslint-to-eslint-config). 7 | 8 | > ⚠️This is an evolving topic. `typescript-eslint-parser` is no longer maintained and [work has recently begun on `typescript-eslint` in the ESLint community](https://eslint.org/blog/2019/01/future-typescript-eslint) to bring ESLint up to full parity and interop with TSLint. 9 | 10 | Follow the TypeScript + ESLint docs at https://github.com/typescript-eslint/typescript-eslint: 11 | 12 | ``` 13 | yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint 14 | ``` 15 | 16 | add a `lint` script to your `package.json`: 17 | 18 | ```json 19 | "scripts": { 20 | "lint": "eslint 'src/**/*.ts'" 21 | }, 22 | ``` 23 | 24 | and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comments): 25 | 26 | ```js 27 | module.exports = { 28 | env: { 29 | es6: true, 30 | node: true, 31 | jest: true, 32 | }, 33 | extends: "eslint:recommended", 34 | parser: "@typescript-eslint/parser", 35 | plugins: ["@typescript-eslint"], 36 | parserOptions: { 37 | ecmaVersion: 2017, 38 | sourceType: "module", 39 | }, 40 | rules: { 41 | indent: ["error", 2], 42 | "linebreak-style": ["error", "unix"], 43 | quotes: ["error", "single"], 44 | "no-console": "warn", 45 | "no-unused-vars": "off", 46 | "@typescript-eslint/no-unused-vars": [ 47 | "error", 48 | { vars: "all", args: "after-used", ignoreRestSiblings: false }, 49 | ], 50 | "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. 51 | "no-empty": "warn", 52 | }, 53 | }; 54 | ``` 55 | 56 | Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. 57 | 58 | More `.eslintrc.json` options to consider with more options you may want for **apps**: 59 | 60 | ```json 61 | { 62 | "extends": [ 63 | "airbnb", 64 | "prettier", 65 | "prettier/react", 66 | "plugin:prettier/recommended", 67 | "plugin:jest/recommended", 68 | "plugin:unicorn/recommended" 69 | ], 70 | "plugins": ["prettier", "jest", "unicorn"], 71 | "parserOptions": { 72 | "sourceType": "module", 73 | "ecmaFeatures": { 74 | "jsx": true 75 | } 76 | }, 77 | "env": { 78 | "es6": true, 79 | "browser": true, 80 | "jest": true 81 | }, 82 | "settings": { 83 | "import/resolver": { 84 | "node": { 85 | "extensions": [".js", ".jsx", ".ts", ".tsx"] 86 | } 87 | } 88 | }, 89 | "overrides": [ 90 | { 91 | "files": ["**/*.ts", "**/*.tsx"], 92 | "parser": "typescript-eslint-parser", 93 | "rules": { 94 | "no-undef": "off" 95 | } 96 | } 97 | ] 98 | } 99 | ``` 100 | 101 | Another great resource is ["Using ESLint and Prettier in a TypeScript Project"](https://dev.to/robertcoopercode/using-eslint-and-prettier-in-a-typescript-project-53jb) by @robertcoopercode. 102 | 103 | Wes Bos is also working on [TypeScript support for his eslint+prettier config.](https://github.com/wesbos/eslint-config-wesbos/issues/68) 104 | 105 | If you're looking for information on Prettier, check out the [Prettier](https://github.com/typescript-cheatsheets/react/blob/main/docs/advanced/misc-concerns.md#prettier) guide. 106 | -------------------------------------------------------------------------------- /docs/hoc/full-example.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: full_example 3 | sidebar_label: Full HOC Example 4 | title: "Full HOC Example" 5 | --- 6 | 7 | > This is an HOC example for you to copy and paste. If certain pieces don't make sense for you, head to [the React HOC Docs intro](https://react-typescript-cheatsheet.netlify.app/docs/hoc/react_hoc_docs/) to get a detailed walkthrough via a complete translation of the React docs in TypeScript. 8 | 9 | Sometimes you want a simple way to inject props from somewhere else (either a global store or a provider) and don't want to continually pass down the props for it. Context is great for it, but then the values from the context can only be used in your `render` function. A HoC will provide these values as props. 10 | 11 | **The injected props** 12 | 13 | ```ts 14 | interface WithThemeProps { 15 | primaryColor: string; 16 | } 17 | ``` 18 | 19 | **Usage in the component** 20 | 21 | The goal is to have the props available on the interface for the component, but subtracted out for the consumers of the component when wrapped in the HoC. 22 | 23 | ```ts 24 | interface Props extends WithThemeProps { 25 | children?: React.ReactNode; 26 | } 27 | 28 | class MyButton extends React.Component { 29 | public render() { 30 | // Render an the element using the theme and other props. 31 | } 32 | 33 | private someInternalMethod() { 34 | // The theme values are also available as props here. 35 | } 36 | } 37 | 38 | export default withTheme(MyButton); 39 | ``` 40 | 41 | **Consuming the Component** 42 | 43 | Now when consuming the component you can omit the `primaryColor` prop or override the one provided through context. 44 | 45 | ```tsx 46 | Hello button // Valid 47 | Hello Button // Also valid 48 | ``` 49 | 50 | **Declaring the HoC** 51 | 52 | The actual HoC. 53 | 54 | ```tsx 55 | export function withTheme( 56 | WrappedComponent: React.ComponentType 57 | ) { 58 | // Try to create a nice displayName for React Dev Tools. 59 | const displayName = 60 | WrappedComponent.displayName || WrappedComponent.name || "Component"; 61 | 62 | // Creating the inner component. The calculated Props type here is the where the magic happens. 63 | const ComponentWithTheme = (props: Omit) => { 64 | // Fetch the props you want to inject. This could be done with context instead. 65 | const themeProps = useTheme(); 66 | 67 | // props comes afterwards so the can override the default ones. 68 | return ; 69 | }; 70 | 71 | ComponentWithTheme.displayName = `withTheme(${displayName})`; 72 | 73 | return ComponentWithTheme; 74 | } 75 | ``` 76 | 77 | Note that the `{...(props as T)}` assertion is needed because of a current bug in TS 3.2 https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046 78 | 79 | Here is a more advanced example of a dynamic higher order component that bases some of its parameters on the props of the component being passed in: 80 | 81 | ```tsx 82 | // inject static values to a component so that they're always provided 83 | export function inject( 84 | Component: React.JSXElementConstructor, 85 | injector: Pick 86 | ) { 87 | return function Injected(props: Omit) { 88 | return ; 89 | }; 90 | } 91 | ``` 92 | 93 | ### Using `forwardRef` 94 | 95 | For "true" reusability you should also consider exposing a ref for your HOC. You can use `React.forwardRef` as documented in [the basic cheatsheet](https://github.com/typescript-cheatsheets/react/blob/main/README.md#forwardrefcreateref), but we are interested in more real world examples. [Here is a nice example in practice](https://gist.github.com/OliverJAsh/d2f462b03b3e6c24f5588ca7915d010e) from @OliverJAsh (note - it still has some rough edges, we need help to test this out/document this). 96 | 97 | ### Supporting `defaultProps` of Wrapped Component 98 | 99 | If this is something you need, please see [the stale discussion we had](https://github.com/typescript-cheatsheets/react/issues/86) and comment with your requirements. We will pick this up again if needed. 100 | -------------------------------------------------------------------------------- /docs/basic/getting-started/context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: context 3 | title: Context 4 | --- 5 | 6 | ## Basic example 7 | 8 | Here's a basic example of creating a context containing the active theme. 9 | 10 | ```tsx 11 | import { createContext } from "react"; 12 | 13 | type ThemeContextType = "light" | "dark"; 14 | 15 | const ThemeContext = createContext("light"); 16 | ``` 17 | 18 | Wrap the components that need the context with a context provider: 19 | 20 | ```tsx 21 | import { useState } from "react"; 22 | 23 | const App = () => { 24 | const [theme, setTheme] = useState("light"); 25 | 26 | return ( 27 | 28 | 29 | 30 | ); 31 | }; 32 | ``` 33 | 34 | Call `useContext` to read and subscribe to the context. 35 | 36 | ```tsx 37 | import { useContext } from "react"; 38 | 39 | const MyComponent = () => { 40 | const theme = useContext(ThemeContext); 41 | 42 | return

The current theme is {theme}.

; 43 | }; 44 | ``` 45 | 46 | ## Without default context value 47 | 48 | If you don't have any meaningful default value, specify `null`: 49 | 50 | ```tsx 51 | import { createContext } from "react"; 52 | 53 | interface CurrentUserContextType { 54 | username: string; 55 | } 56 | 57 | const CurrentUserContext = createContext(null); 58 | ``` 59 | 60 | ```tsx 61 | const App = () => { 62 | const [currentUser, setCurrentUser] = useState({ 63 | username: "filiptammergard", 64 | }); 65 | 66 | return ( 67 | 68 | 69 | 70 | ); 71 | }; 72 | ``` 73 | 74 | Now that the type of the context can be `null`, you'll notice that you'll get a `'currentUser' is possibly 'null'` TypeScript error if you try to access the `username` property. You can use optional chaining to access `username`: 75 | 76 | ```tsx 77 | import { useContext } from "react"; 78 | 79 | const MyComponent = () => { 80 | const currentUser = useContext(CurrentUserContext); 81 | 82 | return

Name: {currentUser?.username}.

; 83 | }; 84 | ``` 85 | 86 | However, it would be preferable to not have to check for `null`, since we know that the context won't be `null`. One way to do that is to provide a custom hook to use the context, where an error is thrown if the context is not provided: 87 | 88 | ```tsx 89 | import { createContext } from "react"; 90 | 91 | interface CurrentUserContextType { 92 | username: string; 93 | } 94 | 95 | const CurrentUserContext = createContext(null); 96 | 97 | const useCurrentUser = () => { 98 | const currentUserContext = useContext(CurrentUserContext); 99 | 100 | if (!currentUserContext) { 101 | throw new Error( 102 | "useCurrentUser has to be used within " 103 | ); 104 | } 105 | 106 | return currentUserContext; 107 | }; 108 | ``` 109 | 110 | Using a runtime type check in this will has the benefit of printing a clear error message in the console when a provider is not wrapping the components properly. Now it's possible to access `currentUser.username` without checking for `null`: 111 | 112 | ```tsx 113 | import { useContext } from "react"; 114 | 115 | const MyComponent = () => { 116 | const currentUser = useCurrentUser(); 117 | 118 | return

Username: {currentUser.username}.

; 119 | }; 120 | ``` 121 | 122 | ### Type assertion as an alternative 123 | 124 | Another way to avoid having to check for `null` is to use type assertion to tell TypeScript you know the context is not `null`: 125 | 126 | ```tsx 127 | import { useContext } from "react"; 128 | 129 | const MyComponent = () => { 130 | const currentUser = useContext(CurrentUserContext); 131 | 132 | return

Name: {currentUser!.username}.

; 133 | }; 134 | ``` 135 | 136 | Another option is to use an empty object as default value and cast it to the expected context type: 137 | 138 | ```tsx 139 | const CurrentUserContext = createContext( 140 | {} as CurrentUserContextType 141 | ); 142 | ``` 143 | 144 | You can also use non-null assertion to get the same result: 145 | 146 | ```tsx 147 | const CurrentUserContext = createContext(null!); 148 | ``` 149 | 150 | When you don't know what to choose, prefer runtime checking and throwing over type asserting. 151 | -------------------------------------------------------------------------------- /docs/basic/getting-started/forward-create-ref.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: forward_and_create_ref 3 | title: forwardRef/createRef 4 | --- 5 | 6 | Check the [Hooks section](https://github.com/typescript-cheatsheets/react/blob/main/README.md#hooks) for `useRef`. 7 | 8 | `createRef`: 9 | 10 | ```tsx 11 | import { createRef, PureComponent } from "react"; 12 | 13 | class CssThemeProvider extends PureComponent { 14 | private rootRef = createRef(); // like this 15 | render() { 16 | return
{this.props.children}
; 17 | } 18 | } 19 | ``` 20 | 21 | `forwardRef`: 22 | 23 | ```tsx 24 | import { forwardRef, ReactNode } from "react"; 25 | 26 | interface Props { 27 | children?: ReactNode; 28 | type: "submit" | "button"; 29 | } 30 | export type Ref = HTMLButtonElement; 31 | 32 | export const FancyButton = forwardRef((props, ref) => ( 33 | 36 | )); 37 | ``` 38 | 39 |
40 | Side note: the ref you get from forwardRef is mutable so you can assign to it if needed. 41 | 42 | This was done [on purpose](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/43265/). You can make it immutable if you have to - assign `React.Ref` if you want to ensure nobody reassigns it: 43 | 44 | ```tsx 45 | import { forwardRef, ReactNode, Ref } from "react"; 46 | 47 | interface Props { 48 | children?: ReactNode; 49 | type: "submit" | "button"; 50 | } 51 | 52 | export const FancyButton = forwardRef( 53 | ( 54 | props: Props, 55 | ref: Ref // <-- here! 56 | ) => ( 57 | 60 | ) 61 | ); 62 | ``` 63 | 64 |
65 | 66 | If you are grabbing the props of a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L770). 67 | 68 | ## Generic forwardRefs 69 | 70 | Read more context in https://fettblog.eu/typescript-react-generic-forward-refs/: 71 | 72 | ### Option 1 - Wrapper component 73 | 74 | ```ts 75 | type ClickableListProps = { 76 | items: T[]; 77 | onSelect: (item: T) => void; 78 | mRef?: React.Ref | null; 79 | }; 80 | 81 | export function ClickableList(props: ClickableListProps) { 82 | return ( 83 |
    84 | {props.items.map((item, i) => ( 85 |
  • 86 | 87 | {item} 88 |
  • 89 | ))} 90 |
91 | ); 92 | } 93 | ``` 94 | 95 | ### Option 2 - Redeclare forwardRef 96 | 97 | ```ts 98 | // Redeclare forwardRef 99 | declare module "react" { 100 | function forwardRef( 101 | render: (props: P, ref: React.Ref) => React.ReactElement | null 102 | ): (props: P & React.RefAttributes) => React.ReactElement | null; 103 | } 104 | 105 | // Just write your components like you're used to! 106 | import { forwardRef, ForwardedRef } from "react"; 107 | 108 | interface ClickableListProps { 109 | items: T[]; 110 | onSelect: (item: T) => void; 111 | } 112 | 113 | function ClickableListInner( 114 | props: ClickableListProps, 115 | ref: ForwardedRef 116 | ) { 117 | return ( 118 |
    119 | {props.items.map((item, i) => ( 120 |
  • 121 | 122 | {item} 123 |
  • 124 | ))} 125 |
126 | ); 127 | } 128 | 129 | export const ClickableList = forwardRef(ClickableListInner); 130 | ``` 131 | 132 | ### Option 3 - Call signature 133 | 134 | ```ts 135 | // Add to `index.d.ts` 136 | interface ForwardRefWithGenerics extends React.FC> { 137 | (props: WithForwardRefProps): ReturnType< 138 | React.FC> 139 | >; 140 | } 141 | 142 | export const ClickableListWithForwardRef: ForwardRefWithGenerics = 143 | forwardRef(ClickableList); 144 | ``` 145 | 146 | Credits: https://stackoverflow.com/a/73795494 147 | 148 | ## More Info 149 | 150 | - https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 151 | 152 | You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react/issues/167). 153 | 154 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 155 | -------------------------------------------------------------------------------- /docs/basic/getting-started/portals.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: portals 3 | title: Portals 4 | --- 5 | 6 | Using `ReactDOM.createPortal`: 7 | 8 | ```tsx 9 | const modalRoot = document.getElementById("modal-root") as HTMLElement; 10 | // assuming in your html file has a div with id 'modal-root'; 11 | 12 | export class Modal extends React.Component<{ children?: React.ReactNode }> { 13 | el: HTMLElement = document.createElement("div"); 14 | 15 | componentDidMount() { 16 | modalRoot.appendChild(this.el); 17 | } 18 | 19 | componentWillUnmount() { 20 | modalRoot.removeChild(this.el); 21 | } 22 | 23 | render() { 24 | return ReactDOM.createPortal(this.props.children, this.el); 25 | } 26 | } 27 | ``` 28 | 29 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWRYmAEQHkBZObXAo9GAWgBNcZchTQQAdgGd4ICHxQAbBBAjwAvHAFoAriCRiYAOgDmSGAFF5SXfoBCATwCSfABQAiGXPk8cK1wEo4FAk4AAkAFWYAGQsrPRgAbgoAeiTAiQkdYDEjOCy4OwgtKDgACxgQeTZgS1KgwI1gADc4AHdgGBLcvgIPBW9lGHxE4XIkAA9qeDR5IODmWQU4cZg9PmDkbgMAYVxIMTi4AG8KOCX5AC5QiOjLazUNCG07gzQuFZi7tz4m-2GTuFE4HEcXowD48y0+mcAWO5FOp16igGBhQYDAqy2JWqLg6wAkBiQ8j8w1OAF8KP9AXs4gB1aryACqYhkkJg0KO-wRCyRKgMRBkjSQmOxzlx+MJxP+5JGpyIYj4SCg7Nh8LgRBgRTEtG4TGYLzeSAACtAYApRVj8WAcGB8WgsfI+HKADRwMUEokkuDS0lAA) 30 | 31 |
32 | Using hooks 33 | 34 | Same as above but using hooks 35 | 36 | ```tsx 37 | import { useEffect, useRef, ReactNode } from "react"; 38 | import { createPortal } from "react-dom"; 39 | 40 | const modalRoot = document.querySelector("#modal-root") as HTMLElement; 41 | 42 | type ModalProps = { 43 | children: ReactNode; 44 | }; 45 | 46 | function Modal({ children }: ModalProps) { 47 | // create div element only once using ref 48 | const elRef = useRef(null); 49 | if (!elRef.current) elRef.current = document.createElement("div"); 50 | 51 | useEffect(() => { 52 | const el = elRef.current!; // non-null assertion because it will never be null 53 | modalRoot.appendChild(el); 54 | return () => { 55 | modalRoot.removeChild(el); 56 | }; 57 | }, []); 58 | 59 | return createPortal(children, elRef.current); 60 | } 61 | ``` 62 | 63 | [View in the TypeScript Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAJQKYEMDGMA0cDecCuAzkgKIBmZSG2RSyZ2y6MAchACZJwC+cZUEEHADkUVBmEBYAFChIsXHDRiUMJAAVoMFABsefAUNHiYAWnaCp0mQHobcFIUL4QwAHYBzOO7gBPCPhQcAAWMCB6ZMA6XMGODnDswABucADuwDDB3uwiIBy6pgIQMMIA3DJoEG6E8HnsuggQxXAAvAkQaC5IbjAAdACO+EhQvgDKSNEY0AAUAEQAxHUFRTCzAJQOhHAAEgAqALIAMiTRIN0w5dbSML5gXPv5OuoCYFttODJwSsFR7GJuAC5ECY2JxLtxLjIyPg3BhgFU4A96jppng0D8dH9ujwgUjdM8IK8Nh9pF87EoVGoEsk4BMkGcenAqjpfEzYVwiO4vGIyJ8lFUarSdPRWgRiPQADx7I4AEWSJ3p5zgAB84G58DodAA+abqzVrS5fYBkODTACEE3ovU6UH+MA2lqQZGtgTtoosnQZfWUqDUCq9c0SSXWkNJYtIFCoMGm0w2LS1uD5X0q1XgE1FjudNrtZtKcHJbiqpj1ekcxFg8LccAARlQULRvPB0pq1UgksMa1wS0m4EthU0+igwHc3OwAMIY9jTCYGntiGCBKux1oJklfde9x6NYq9MR5dsT37TnSzsNfCF87jYADaAF1T3z54uKb6NFpdNN0b9-thMy7becp7cDIIHSOSRAoB4SCgdCsIwJWcAAILDsua78qmcDXoQwQQKkeI6NgxAwKMOF4Y8t6ikwGC9LQozaGo0xkLoxCnl8T5QEuPYSkGWo9mS9j+PgSgoFWzEQHAYD4PAmTAFsPiCUENSqMAaAhGEERRNBZ7rtxNLAOwLSzH2hQDrMWoSjYPF8bg2G4fhcAAGQOaa1lfBK+G8dpG5uUGrneTUvjRC0OBod5YXUoQYA6CgvhArMHhQPpsyYH5YVRegSAAJJqCAhBxWg5zDMlqXecESDAB4oRxQAjAADLVSTBMVXnhV86TsJkQLCHVDXBMIKUta11boAA1glASjnFUAeMN0y1Zg82Lb01VrM1rVhQAXplo5IAAHkCACcB0Det67cMBg3rp5p1fJlwhCCgm7ImaOCzHAswXTdcAStWUkwAiAVBSFw1oGNAgwuwcVgEOvgoKkPxgB9vBVGOOgqSNwXLvGcBESRdmPIxzFIGs3BamgOgQMQFm-TA-1uNd60WVZl0WR51kk9ZP1-QiKNo6DmNxgmuOkfh0wwFAQwk1qtmpIijzU9z9PWeSYiChAJoKQ4w5cZZyQM2sMjcEAA) 64 | 65 |
66 | 67 | Modal Component Usage Example: 68 | 69 | ```tsx 70 | import { useState } from "react"; 71 | 72 | function App() { 73 | const [showModal, setShowModal] = useState(false); 74 | 75 | return ( 76 |
77 | // you can also put this in your static html file 78 | 79 | {showModal && ( 80 | 81 |
91 | I'm a modal!{" "} 92 | 98 |
99 |
100 | )} 101 | 102 | // rest of your app 103 |
104 | ); 105 | } 106 | ``` 107 | 108 |
109 | 110 | Context of Example 111 | 112 | This example is based on the [Event Bubbling Through Portal](https://reactjs.org/docs/portals.html#event-bubbling-through-portals) example of React docs. 113 | 114 |
115 | -------------------------------------------------------------------------------- /docs/basic/getting-started/function-components.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: function_components 3 | title: Function Components 4 | --- 5 | 6 | These can be written as normal functions that take a `props` argument and return a JSX element. 7 | 8 | ```tsx 9 | // Declaring type of props - see "Typing Component Props" for more examples 10 | type AppProps = { 11 | message: string; 12 | }; /* use `interface` if exporting so that consumers can extend */ 13 | 14 | // Easiest way to declare a Function Component; return type is inferred. 15 | const App = ({ message }: AppProps) =>
{message}
; 16 | 17 | // You can choose to annotate the return type so an error is raised if you accidentally return some other type 18 | const App = ({ message }: AppProps): React.JSX.Element =>
{message}
; 19 | 20 | // You can also inline the type declaration; eliminates naming the prop types, but looks repetitive 21 | const App = ({ message }: { message: string }) =>
{message}
; 22 | 23 | // Alternatively, you can use `React.FunctionComponent` (or `React.FC`), if you prefer. 24 | // With latest React types and TypeScript 5.1. it's mostly a stylistic choice, otherwise discouraged. 25 | const App: React.FunctionComponent<{ message: string }> = ({ message }) => ( 26 |
{message}
27 | ); 28 | // or 29 | const App: React.FC = ({ message }) =>
{message}
; 30 | ``` 31 | 32 | > Tip: You might use [Paul Shen's VS Code Extension](https://marketplace.visualstudio.com/items?itemName=paulshen.paul-typescript-toolkit) to automate the type destructure declaration (incl a [keyboard shortcut](https://twitter.com/_paulshen/status/1392915279466745857?s=20)). 33 | 34 |
35 | 36 | Why is React.FC not needed? What about React.FunctionComponent/React.VoidFunctionComponent? 37 | 38 | You may see this in many React+TypeScript codebases: 39 | 40 | ```tsx 41 | const App: React.FunctionComponent<{ message: string }> = ({ message }) => ( 42 |
{message}
43 | ); 44 | ``` 45 | 46 | However, the general consensus today is that `React.FunctionComponent` (or the shorthand `React.FC`) is not needed. If you're still using React 17 or TypeScript lower than 5.1, it is even [discouraged](https://github.com/facebook/create-react-app/pull/8177). This is a nuanced opinion of course, but if you agree and want to remove `React.FC` from your codebase, you can use [this jscodeshift codemod](https://github.com/gndelia/codemod-replace-react-fc-typescript). 47 | 48 | Some differences from the "normal function" version: 49 | 50 | - `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). 51 | 52 | - It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps`. 53 | 54 | - Note that there are some known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react/issues/87). We maintain a separate `defaultProps` section you can also look up. 55 | 56 | - Before the [React 18 type updates](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210), `React.FunctionComponent` provided an implicit definition of `children` (see below), which was heavily debated and is one of the reasons [`React.FC` was removed from the Create React App TypeScript template](https://github.com/facebook/create-react-app/pull/8177). 57 | 58 | ```tsx 59 | // before React 18 types 60 | const Title: React.FunctionComponent<{ title: string }> = ({ 61 | children, 62 | title, 63 | }) =>
{children}
; 64 | ``` 65 | 66 |
67 | (Deprecated)Using React.VoidFunctionComponent or React.VFC instead 68 | 69 | In [@types/react 16.9.48](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643), the `React.VoidFunctionComponent` or `React.VFC` type was added for typing `children` explicitly. 70 | However, please be aware that `React.VFC` and `React.VoidFunctionComponent` were deprecated in React 18 (https://github.com/DefinitelyTyped/DefinitelyTyped/pull/59882), so this interim solution is no longer necessary or recommended in React 18+. 71 | 72 | Please use regular function components or `React.FC` instead. 73 | 74 | ```ts 75 | type Props = { foo: string }; 76 | 77 | // OK now, in future, error 78 | const FunctionComponent: React.FunctionComponent = ({ 79 | foo, 80 | children, 81 | }: Props) => { 82 | return ( 83 |
84 | {foo} {children} 85 |
86 | ); // OK 87 | }; 88 | 89 | // Error now, in future, deprecated 90 | const VoidFunctionComponent: React.VoidFunctionComponent = ({ 91 | foo, 92 | children, 93 | }) => { 94 | return ( 95 |
96 | {foo} 97 | {children} 98 |
99 | ); 100 | }; 101 | ``` 102 | 103 |
104 | 105 | - _In the future_, it may automatically mark props as `readonly`, though that's a moot point if the props object is destructured in the parameter list. 106 | 107 | In most cases it makes very little difference which syntax is used, but you may prefer the more explicit nature of `React.FunctionComponent`. 108 | 109 |
110 | -------------------------------------------------------------------------------- /docs/migration/from-js.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: from_js 3 | title: From JS 4 | --- 5 | 6 | ## Automated JS to TS Conversion 7 | 8 | - [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) ([used by Codecademy](https://mobile.twitter.com/JoshuaKGoldberg/status/1159090281314160640)) 9 | - [TypeWiz](https://github.com/urish/typewiz) 10 | - [js-to-ts-converter](https://github.com/gregjacobs/js-to-ts-converter) 11 | - [TS-migrate](https://github.com/airbnb/ts-migrate) used in [Airbnb's conversion](https://medium.com/airbnb-engineering/ts-migrate-a-tool-for-migrating-to-typescript-at-scale-cd23bfeb5cc) 12 | - [dts-gen](https://github.com/microsoft/dts-gen) - `dts-gen` is a tool that generates TypeScript definition files (.d.ts) from any JavaScript object. 13 | 14 | for JSON - http://json2ts.com/ generate TypeScript interfaces from JSON 15 | 16 | ## Manual JS to TS Conversion 17 | 18 | the "Just Renaming" strategy 19 | 20 | - OSX/Linux: `find src -name "*.js" -exec sh -c 'mv "$0" "${0%.js}.tsx"' {} \;` 21 | 22 | You can either load typescript files with webpack, or use the `tsc` compiler to compile your TS files to JS side by side. The basic `tsconfig.json` is: 23 | 24 | ```json 25 | { 26 | "compilerOptions": { 27 | "allowJs": true 28 | } 29 | } 30 | ``` 31 | 32 | Then you will want to enable it to check JS: 33 | 34 | ```json 35 | { 36 | "compilerOptions": { 37 | "allowJs": true, 38 | "checkJs": true 39 | } 40 | } 41 | ``` 42 | 43 | If you have a large codebase and this throws too many errors at once, you can opt out problematic files with `//@ts-nocheck`, or instead turn off `checkJs` and add a `//@ts-check` directive at the top of each regular JS file. 44 | 45 | TypeScript should throw up some egregious errors here which should be easy to fix. 46 | 47 | Once you are done, swallow the red pill by turning off implicit `any`'s: 48 | 49 | ```js 50 | { 51 | "compilerOptions": { 52 | "allowJs": true, 53 | "checkJs": true, 54 | "noImplicitAny": true // or "strict": true 55 | } 56 | } 57 | ``` 58 | 59 | This will raise a bunch of type errors and you can start converting files to TS or (optionally) use [JSDoc annotations](https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html) in your JS. 60 | 61 | A common practice here is using an ambient TODO type alias for `any` so you can keep track of what you need to come back to: 62 | 63 | ```ts 64 | type TODO_TYPEME = any; 65 | export function myFunc(foo: TODO_TYPEME, bar: TODO_TYPEME): number { 66 | // ... 67 | } 68 | ``` 69 | 70 | Gradually add [more `strict` mode flags](https://www.typescriptlang.org/docs/handbook/compiler-options.html) like `noImplicitThis`, `strictNullChecks`, and so on until you can eventually just run in full strict mode with no js files left: 71 | 72 | ```js 73 | { 74 | "compilerOptions": { 75 | "strict": true 76 | } 77 | } 78 | ``` 79 | 80 | ## 3 Step Process 81 | 82 | ![image](https://user-images.githubusercontent.com/6764957/91499410-f1399080-e8f3-11ea-86f8-431266af713b.png) 83 | 84 | https://speakerdeck.com/amhinson/convert-a-react-native-project-to-typescript-in-10-minutes?slide=23 85 | 86 | ## More resources 87 | 88 | - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) - their [ts-migrate tool here](https://medium.com/airbnb-engineering/ts-migrate-a-tool-for-migrating-to-typescript-at-scale-cd23bfeb5cc) 89 | - [Scaling TypeScript lessons from Bloomberg](https://www.techatbloomberg.com/blog/10-insights-adopting-typescript-at-scale/) 90 | - [Migrating a `create-react-app`/`react-scripts` app to TypeScript](https://facebook.github.io/create-react-app/docs/adding-typescript) - don't use `react-scripts-ts` 91 | - [Migrating an EJECTED CRA app to TS](https://spin.atomicobject.com/2018/07/04/migrating-cra-typescript/) 92 | - [Lyft's JS to TS migration tool](https://github.com/lyft/react-javascript-to-typescript-transform) (includes PropTypes migration) 93 | - [Hootsuite][hootsuite] 94 | - [Etsy's Journey to TypeScript](https://codeascraft.com/2021/11/08/etsys-journey-to-typescript/) 95 | - [Storybook's migration (PR)](https://github.com/storybooks/storybook/issues/5030) 96 | - [How we migrated a 200K+ LOC project to TypeScript and survived to tell the story][coherentlabs] - Coherent Labs - using `grunt-ts`, jQuery and Kendo UI 97 | - incrementally adding strict null checks https://code.visualstudio.com/blogs/2019/05/23/strict-null 98 | 99 | Old content that is possibly out of date 100 | 101 | - [Incrementally Migrating JS to TS][clayallsop] (old) 102 | - [Microsoft's TypeScript React Conversion Guide][mstsreactconversionguide] (old) 103 | 104 | [clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 "Incrementally Migrating JavaScript to TypeScript" 105 | [pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 "Migrating a Babel project to TypeScript" 106 | [tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ "Benefits of gradual strong typing in JavaScript" 107 | [entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" 108 | [mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide "TypeScript React Conversion Guide" 109 | [coherentlabs]: https://hashnode.com/post/how-we-migrated-a-200k-loc-project-to-typescript-and-survived-to-tell-the-story-ciyzhikcc0001y253w00n11yb "How we migrated a 200K+ LOC project to TypeScript and survived to tell the story" 110 | [hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" 111 | -------------------------------------------------------------------------------- /code-of-conduct.md: -------------------------------------------------------------------------------- 1 | # Our Code of Conduct 2 | 3 | This code of conduct outlines our expectations for participants within 4 | the [typescript-cheatsheets/react](https://github.com/typescript-cheatsheets/react) repository, as well as steps to 5 | reporting unacceptable behavior. We are committed to providing a welcoming and inspiring community for all and expect 6 | our code of conduct to be honored. Anyone who violates this code of conduct may be banned from the community. 7 | 8 | Our open source repository strives to: 9 | 10 | - **Be friendly and patient.** 11 | - **Be welcoming**: We strive to be a community that welcomes and supports people of all backgrounds and identities. 12 | This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration 13 | status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, 14 | size, family status, political belief, religion, and mental and physical ability. 15 | - **Be considerate**: Your work will be used by other people, and you in turn will depend on the work of others. Any 16 | decision you take will affect users and colleagues, and you should take those consequences into account when making 17 | decisions. Remember that we're a world-wide community, so you might not be communicating in someone else's primary 18 | language. 19 | - **Be respectful**: Not all of us will agree all the time, but disagreement is no excuse for poor behavior and poor 20 | manners. We might all experience some frustration now and then, but we cannot allow that frustration to turn into a 21 | personal attack. It’s important to remember that a community where people feel uncomfortable or threatened is not a 22 | productive one. 23 | - **Be careful in the words that you choose**: we are a community of professionals, and we conduct ourselves 24 | professionally. Be kind to others. Do not insult or put down other participants. Harassment and other exclusionary 25 | behavior aren't acceptable. This includes, but is not limited to: 26 | - Violent threats or language directed against another person. 27 | - Discriminatory jokes and language. 28 | - Posting sexually explicit or violent material. 29 | - Posting (or threatening to post) other people's personally identifying information ("doxing"). 30 | - Personal insults, especially those using racist or sexist terms. 31 | - Unwelcome sexual attention. 32 | - Advocating for, or encouraging, any of the above behavior. 33 | - Repeated harassment of others. In general, if someone asks you to stop, then stop. 34 | - **When we disagree, try to understand why**: Disagreements, both social and technical, happen all the time. It is 35 | important that we resolve disagreements and differing views constructively. Remember that we’re different. The 36 | strength of our community comes from its diversity, people from a wide range of backgrounds. Different people have 37 | different perspectives on issues. Being unable to understand why someone holds a viewpoint doesn’t mean that they’re 38 | wrong. Don’t forget that it is human to err and blaming each other doesn’t get us anywhere. Instead, focus on helping 39 | to resolve issues and learning from mistakes. 40 | 41 | This code is not exhaustive or complete. It serves to distill our common understanding of a collaborative, shared 42 | environment, and goals. We expect it to be followed in spirit as much as in the letter. 43 | 44 | ### Diversity Statement 45 | 46 | We encourage everyone to participate and are committed to building a repository for all. Although we may not be able to 47 | satisfy everyone, we all agree that everyone is equal. Whenever a participant has made a mistake, we expect them to take 48 | responsibility for it. If someone has been harmed or offended, it is our responsibility to listen carefully and 49 | respectfully, and do our best to right the wrong. 50 | 51 | Although this list cannot be exhaustive, we explicitly honor diversity in age, gender, gender identity or expression, 52 | culture, ethnicity, language, national origin, political beliefs, profession, race, religion, sexual orientation, 53 | socioeconomic status, and technical ability. We will not tolerate discrimination based on any of the protected 54 | characteristics above, including participants with disabilities. 55 | 56 | ### Reporting Issues 57 | 58 | If you experience or witness unacceptable behavior—or have any other concerns—please report it by contacting us (This 59 | repo is maintained by [@swyx](https://twitter.com/swyx), [@eps1lon](https://twitter.com/sebsilbermann) 60 | and [@filiptammergard](https://twitter.com/tammergard).). All reports will be handled with discretion. In your report 61 | please include: 62 | 63 | - Your contact information. 64 | - Names (real, nicknames, or pseudonyms) of any individuals involved. If there are additional witnesses, please include 65 | them as well. Your account of what occurred, and if you believe the incident is ongoing. If there is a publicly 66 | available record (e.g. a mailing list archive or a public IRC logger), please include a link. 67 | - Any additional information that may be helpful. 68 | 69 | After filing a report, a representative will contact you personally. If the person who is harassing you is part of the 70 | response team, they will recuse themselves from handling your incident. A representative will then review the incident, 71 | follow up with any additional questions, and make a decision as to how to respond. We will respect confidentiality 72 | requests for the purpose of protecting victims of abuse. 73 | 74 | Anyone asked to stop unacceptable behavior is expected to comply immediately. If an individual engages in unacceptable 75 | behavior, the representative may take any action they deem appropriate, up to and including a permanent ban from our 76 | community without warning. 77 | 78 | ## Version 79 | 80 | This is version 1.0.0 81 | 82 | ## Thanks 83 | 84 | This code of conduct is based on the [Open Code of Conduct](https://github.com/todogroup/opencodeofconduct) from 85 | the [TODOGroup](http://todogroup.org). 86 | 87 | We are thankful for their work and all the communities who have paved the way with code of conducts. 88 | -------------------------------------------------------------------------------- /website/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | const { themes } = require("prism-react-renderer"); 2 | 3 | // List of projects/orgs using your project for the users page. 4 | const users = [ 5 | { 6 | caption: "Docusaurus", 7 | image: "https://docusaurus.io/img/docusaurus.svg", 8 | infoLink: "https://docusaurus.io/", 9 | pinned: true, 10 | }, 11 | ]; 12 | 13 | const setupDoc = "docs/basic/setup"; 14 | 15 | module.exports = { 16 | favicon: "img/icon.png", 17 | title: "React TypeScript Cheatsheets", // Title for your website. 18 | tagline: 19 | "Cheatsheets for experienced React developers getting started with TypeScript", 20 | url: "https://react-typescript-cheatsheet.netlify.app", // Your website URL 21 | baseUrl: "/", 22 | projectName: "react-typescript-cheatsheet", 23 | organizationName: "typescript-cheatsheets", 24 | 25 | presets: [ 26 | [ 27 | "@docusaurus/preset-classic", 28 | { 29 | theme: { 30 | customCss: require.resolve("./src/css/custom.css"), 31 | }, 32 | docs: { 33 | // Docs folder path relative to website dir. 34 | path: "../docs", 35 | // Sidebars file relative to website dir. 36 | sidebarPath: require.resolve("./sidebars.json"), 37 | editUrl: 38 | "https://github.com/typescript-cheatsheets/react/tree/main/docs", 39 | }, 40 | // ... 41 | }, 42 | ], 43 | ], 44 | 45 | themeConfig: { 46 | colorMode: { 47 | defaultMode: "dark", 48 | }, 49 | 50 | image: 51 | "https://user-images.githubusercontent.com/6764957/53868378-2b51fc80-3fb3-11e9-9cee-0277efe8a927.png", 52 | 53 | // Equivalent to `docsSideNavCollapsible`. 54 | // sidebarCollapsible: false, 55 | 56 | prism: { 57 | defaultLanguage: "typescript", 58 | theme: themes.github, 59 | darkTheme: themes.dracula, 60 | }, 61 | 62 | navbar: { 63 | title: "React TypeScript Cheatsheet", 64 | logo: { 65 | alt: "Logo", 66 | src: "img/icon.png", 67 | }, 68 | items: [ 69 | { 70 | to: setupDoc, 71 | label: "Docs", 72 | position: "right", 73 | }, 74 | { 75 | to: "help", 76 | label: "Help", 77 | position: "right", 78 | }, 79 | { 80 | to: "https://discord.gg/wTGS5z9", 81 | label: "Discord", 82 | position: "right", 83 | }, 84 | // {to: 'blog', label: 'Blog', position: 'right'}, 85 | ], 86 | }, 87 | 88 | footer: { 89 | style: "dark", 90 | logo: { 91 | alt: "TypeScript Cheatsheets Logo", 92 | src: "img/icon.png", 93 | // maxWidth: 128, 94 | // style: { maxWidth: 128, maxHeight: 128 }, 95 | }, 96 | copyright: `Copyright © ${new Date().getFullYear()} TypeScript Cheatsheets`, 97 | links: [ 98 | { 99 | title: "Docs", 100 | items: [ 101 | { 102 | label: "Introduction", 103 | to: setupDoc, 104 | }, 105 | { 106 | label: "High Order Component (HOC)", 107 | to: "docs/hoc", 108 | }, 109 | { 110 | label: "Advanced Guides", 111 | to: "docs/advanced", 112 | }, 113 | { 114 | label: "Migrating", 115 | to: "docs/migration", 116 | }, 117 | ], 118 | }, 119 | { 120 | title: "Community", 121 | items: [ 122 | { 123 | label: "Stack Overflow", 124 | href: "https://stackoverflow.com/questions/tagged/typescript", 125 | }, 126 | { 127 | label: "User Showcase", 128 | to: "users", 129 | }, 130 | { 131 | label: "Help", 132 | to: "help", 133 | }, 134 | { 135 | label: "Contributors", 136 | to: "contributors", 137 | }, 138 | { 139 | label: "Contributing", 140 | to: "contributing", 141 | }, 142 | ], 143 | }, 144 | { 145 | title: "More", 146 | items: [ 147 | { 148 | label: "GitHub", 149 | href: "https://github.com/typescript-cheatsheets/react", 150 | }, 151 | { 152 | html: ` 153 | GitHub stars 154 | `, 155 | }, 156 | { 157 | // label: "Discord", 158 | html: ` 159 | Discord 160 | `, 161 | }, 162 | { 163 | // label: "Spread the word", 164 | html: ` 165 | X 166 | `, 167 | }, 168 | ], 169 | }, 170 | ], 171 | }, 172 | 173 | algolia: { 174 | apiKey: "9a22585d1841d2fa758da919cd08a764", 175 | indexName: "react-typescript-cheatsheet", 176 | appId: "J65EL4UPXZ", 177 | algoliaOptions: { 178 | //... }, 179 | }, 180 | }, 181 | }, 182 | 183 | customFields: { 184 | firstDoc: setupDoc, 185 | 186 | // TODO useless user showcase page ? 187 | users, 188 | addUserUrl: 189 | "https://github.com/typescript-cheatsheets/react/blob/main/website/docusaurus.config.js", 190 | }, 191 | }; 192 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /.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 | "login": "filiptammergard", 219 | "name": "Filip Tammergård", 220 | "avatar_url": "https://avatars.githubusercontent.com/u/44197016?v=4", 221 | "profile": "https://tammergard.se", 222 | "contributions": [ 223 | "doc", 224 | "code" 225 | ] 226 | }, 227 | { 228 | "login": "jsjoeio", 229 | "name": "Joe Previte", 230 | "avatar_url": "https://avatars.githubusercontent.com/u/3806031?v=4", 231 | "profile": "https://englishbyte.com", 232 | "contributions": [ 233 | "doc" 234 | ] 235 | }, 236 | { 237 | "login": "sasirura", 238 | "name": "Sasiru Ravihansa", 239 | "avatar_url": "https://avatars.githubusercontent.com/u/42542164?v=4", 240 | "profile": "https://github.com/sasirura", 241 | "contributions": [ 242 | "doc" 243 | ] 244 | } 245 | ], 246 | "contributorsPerLine": 7, 247 | "projectName": "react", 248 | "projectOwner": "typescript-cheatsheets", 249 | "repoType": "github", 250 | "repoHost": "https://github.com", 251 | "skipCi": true, 252 | "commitType": "docs" 253 | } 254 | -------------------------------------------------------------------------------- /docs/hoc/excluding-props.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: excluding_props 3 | sidebar_label: Excluding Props 4 | title: "Section 2: Excluding Props" 5 | --- 6 | 7 | This is covered in passing in Section 1 but we focus on it here as it is such a common issue. HOCs often inject props to premade components. The problem we want to solve is having the HOC-wrapped-component exposing a type that reflects the reduced surface area of props - without manually retyping the HOC every time. This involves some generics, fortunately with some helper utilities. 8 | 9 | Say we have a component: 10 | 11 | ```tsx 12 | type DogProps { 13 | name: string 14 | owner: string 15 | } 16 | function Dog({name, owner}: DogProps) { 17 | return
Woof: {name}, Owner: {owner}
18 | } 19 | ``` 20 | 21 | And we have a `withOwner` HOC that injects the `owner`: 22 | 23 | ```tsx 24 | const OwnedDog = withOwner("swyx")(Dog); 25 | ``` 26 | 27 | We want to type `withOwner` such that it will pass through the types of any component like `Dog`, into the type of `OwnedDog`, minus the `owner` property it injects: 28 | 29 | ```tsx 30 | typeof OwnedDog; // we want this to be equal to { name: string } 31 | 32 | ; // this should be fine 33 | ; // this should have a typeError 34 | ; // this should be fine 35 | 36 | // and the HOC should be reusable for completely different prop types! 37 | 38 | type CatProps = { 39 | lives: number; 40 | owner: string; 41 | }; 42 | function Cat({ lives, owner }: CatProps) { 43 | return ( 44 |
45 | {" "} 46 | Meow: {lives}, Owner: {owner} 47 |
48 | ); 49 | } 50 | 51 | const OwnedCat = withOwner("swyx")(Cat); 52 | 53 | ; // this should be fine 54 | ; // this should have a typeError 55 | ; // this should be fine 56 | ``` 57 | 58 | So how do we type `withOwner`? 59 | 60 | 1. We get the types of the component: `keyof T` 61 | 2. We `Exclude` the property we want to mask: `Exclude`, this leaves you with a list of names of properties you want on the wrapped component e.g. `name` 62 | 3. (optional) Use intersection types if you have more to exclude: `Exclude` 63 | 4. Names of properties aren't quite the same as properties themselves, which also have an associated type. So we use this generated list of names to `Pick` from the original props: `Pick>`, this leaves you with the new, filtered props, e.g. `{ name: string }` 64 | 5. (optional) Instead of writing this manually each time, we could use this utility: `type Omit = Pick>` 65 | 6. Now we write the HOC as a generic function: 66 | 67 | ```tsx 68 | function withOwner(owner: string) { 69 | return function ( 70 | Component: React.ComponentType 71 | ) { 72 | return function (props: Omit): React.JSX.Element { 73 | const newProps = { ...props, owner } as T; 74 | return ; 75 | }; 76 | }; 77 | } 78 | ``` 79 | 80 | (_[Link to TS Playground](https://www.typescriptlang.org/play/?strictFunctionTypes=false&jsx=1&ssl=1&ssc=1&pln=47&pc=49#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wG4AoczAVwDsNgJa4B3YGACwHkXakoAFBF78AXHADOMKMFoBzAJRwA3uThwiMalGY16MRswA8AFThIAHjCS0AJhJVxhfKOKkz5cAL4A+AWvU4AGFcSD5aGHFkdBgAOhDwJhsYEwBPMCRTHwClVUCNJC0dOD0GJjgBMBwwCXEuEHZTABoCZ358HyVxACkAZQANWIBRABskEGSVAPyAehmAQTgYdKQ4NAh+NEM4YAc+NDQkCQkUKFS4ACMkNBRqCVW0jN60GTB4Ww2JWgByeABrWjCJYcFDwTireqNEwtfBtKAdOAAahUcPEsXRXjgAF44CZpoF1rQpHA+CwAArVBw45RwdGxKoQGotOHeOAoBwmCj5dSabTGBJhSbKOmkimMiSYmY+LmBLwyuXkLyUZYZYKgsU1bFTdQjYAANyO4lo1BAVygMtRkmksjkFAVpQM5SCoIENN1BokzJEUG84mdMA1ElyAV5xX8+SMtn12W5KnwBCVsYAskhhOJlO6jl4WjwXOm4YnAkYZlG9TG4Ao7ZRCcTc0hbP6tWxOHXBPgJCxUhZ8AoBP7K5QjI3MxIscoAJyYuFY9ud7twKWkBczYG7SQcCDUEa2S6rTCyJDkIx1huguAjseTpzemcdrvxxfL1cOCQbrc7kEGtlLFZDKA4KAjxPYd9SOS9JWlJ9ODXV9N23XcSgPShyBVVYABEIDkQNtRJFAJjca15ACS13BtRUqDoMpmAwuRXVoPCkC9FwvHEGjA2DHlCj5OBI2jOAAHUIAgTB03oiZszgVt829Lxi1LbIlRreATxopt2G4b0BFne9exogdB1UsSkBnfcPnjadtPnR85mfdc4J3K5EL4ICRFsQyGJM4AzOvFxbznB9IJs6CXzfeDP1WFAfwyP8AJcvg3Mw3CJk87zrJXYK7PfBD9z4IA)_) 81 | 82 | Note that we need to do a type coercion here. 83 | 84 | This is because TypeScript does not know that merging `Omit` and `{owner: "whatever"}` is the same as `T`. 85 | 86 | [See this GitHub issue for more.](https://github.com/microsoft/TypeScript/issues/35858) 87 | 88 | ## Generic solution 89 | 90 | The above snippet can be modified to create a generic solution to inject any arbitrary props; 91 | 92 | ```typescript 93 | function withInjectedProps>( 94 | injectedProps: U 95 | ) { 96 | return function (Component: React.ComponentType) { 97 | return function (props: Omit): React.JSX.Element { 98 | //A type coercion is necessary because TypeScript doesn't know that the Omit + {...injectedProps} = T 99 | const newProps = { ...props, ...injectedProps } as T; 100 | return ; 101 | }; 102 | }; 103 | } 104 | ``` 105 | 106 | (_[Link to TS Playground](https://www.typescriptlang.org/play?strictFunctionTypes=false&jsx=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wG4AoczAVwDsNgJa4B3YGACwElaArJDEgAmABRxgAzgB4AqnCQAPGElpCJiAdCFSJMKMFoBzADRw6Aa1oQWtAHy24ACgP9Bo8RIBccGQEo4AN7kcHBEMNRQzDT0MIzMUgAq8koqaj62jsEhcADCuJC0KjDeyOgwAHR54ExFCQCeYEiJtln+QdmhSOGRcNEMTE5gHt4A8iDsiabmSHUQmOn+3gBSAMoAGuUAogA2SCBFgVkdAPTHAIJwMA1IcGgQSFBocXDA6oVoaEgSEihQdXAAIwEKGoEhu9UaKzQ+jA8CE9wktAA5PBLNZLhwUPBODcxhMEqZ8NZClB8A4ANSBYkPbzlOkAXzgAF44Akjtk7rRdHBCiwxBBJMzAnA6eUhgKJKZRS4BMp3BK4IyUOoEhQOiEwhF4lUCgcAqLefzJIzjrY1dl6ebLeR6ZQro1clijeoWe04NtgAA3L7eWjUEBAqDm6lQby6fRGCjWvqxAY5LGOALur1fUwhxXeeMwZ1tLKanqZDpSIRelrqwL4Ai28sAWSQ1m8AQ93ok9NMIxsNKpnag1eyUmOJc9ZbgvijduucBE2xQhWzHiFbtoKH2Yb0BkMpDgNsoMee09nXUTy-2jO8B7nOcOGq6Wqc7OLpbgjSgEiYbxXN1egRPSHpA6HEcx23W1yE5bkO0KIQsyFNhOB4Vw5WdRMQ28fAAQgAF8HpXxHCzYDKCkGDmy+JkAgATkZEMmXwCQWDqBRK1NLdTgxb8JA4CBqG2IRARuTADCQcgpEg4RiJTCQyMouBqNo+jGLgZjFOONj1A4rieLgTFvTgFBLmuTYoBwKBhNE6CsWTFspJNM1lNUuB1O43igV6QTKHA+AzIvLpYPYbg+FlYRkICVCCAwrCcMcbyYGA1jNgURo3HkIzoDgABaXTtk4LjDA4Ux2CRN4IHgMBfliNBuN+bZ-iIFAhBQAFdnKbcgA)_) 107 | 108 | ## Without coercion 109 | 110 | ```typescript 111 | function withOwner(owner: string) { 112 | return function ( 113 | Component: React.ComponentType 114 | ): React.ComponentType & { owner?: never }> { 115 | return function (props) { 116 | const newProps = { ...props, owner }; 117 | return ; 118 | }; 119 | }; 120 | } 121 | ``` 122 | 123 | (_[Link to TS Playground](https://www.typescriptlang.org/play?strictFunctionTypes=false&jsx=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wG4AoczAVwDsNgJa4B3YGACwHkXakoAFBF78AXHADOMKMFoBzAJRwA3uThwiMalGY16MRswA8AFThIAHjCS0AJhJVxhfKOKkz5cAL4A+AWvU4AGFcSD5aGHFkdBgAOhDwJhsYEwBPMCRTHwCFKOI4hLDktIyjLhB2UwAaAmd+fB84ADIVOqgAfnE+ADd+XxUA9U1tXToGJjgBMBwwCSVVQMC0Jik4PhYABRmHAF5HWIPpiFmatu8KRaGkLR04I0KkiJUD2PWt44kvOAB6HwvArz-QHkLyUGDpJDBFAwd6zOB7BZwAA2wF6Ei61BAACN+P82m5pLI5BRgXpxswgtCBMpkaikBJTiIoN5xJSYdt5gFhrd-IsjLZUdlLip8ARQcKALJIYTiZQotFeGo8FyytriwJGb4C7pCuAKEmUZa0VbKpC2Nnw1jsbhMgT4CQsVIWfAKARs-WUe7Q2lonbKACcXzaO3tjudPz+P2+cE4wAcEg4EGoSNscBxcEwsiQ5DKInN3vl9L9gacTJDDqdot+pCjMY4cckieTqY4KF6cBQMYhAFEoDgoDnTfn4IWJMWvtXa7H402U2nIZm+JRyOCMnAACIQOSwhyI2goEBIAkeOQBfGSQnyEFUMYGCabuTU-eHxkuLziB87zlXG7GbWNAB1CAIEwWVnyQRU4FNVxWiZLxNX-a8jRNPMH0tNhOGgu0K2dV0Hw9T00PAkNM1sCBRWDUNKwjGtvmjadGyTOd00XbNcz4WwiIPJASOAMiKLLKjw0nOi6wbBMmJbNtIU7VckF7ftB1Qrc1m43j+Joqd6xnST5wzLMgA)_) 124 | 125 | ## Learn More 126 | 127 | We will need to extract lessons from here in future but here they are: 128 | 129 | - https://medium.com/@xfor/typescript-react-hocs-context-api-cb46da611f12 130 | - https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb 131 | - https://www.matthewgerstman.com/tech/ts-tricks-higher-order-components/ 132 | -------------------------------------------------------------------------------- /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`, `React.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/pull/69): I discourage the use of most `...Element` types because of how black-boxy `React.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/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 | - [Original tweet](https://twitter.com/hswolff/status/1133759319571345408) 11 | - More info can also be found in [this article](https://medium.com/@matanbobi/react-defaultprops-is-dying-whos-the-contender-443c19d9e7f1) 12 | 13 | The consensus is to use object default values. 14 | 15 | Function Components: 16 | 17 | ```tsx 18 | type GreetProps = { age?: number }; 19 | 20 | const Greet = ({ age = 21 }: GreetProps) => // etc 21 | ``` 22 | 23 | Class Components: 24 | 25 | ```tsx 26 | type GreetProps = { 27 | age?: number; 28 | }; 29 | 30 | class Greet extends React.Component { 31 | render() { 32 | const { age = 21 } = this.props; 33 | /*...*/ 34 | } 35 | } 36 | 37 | let el = ; 38 | ``` 39 | 40 | ## Typing `defaultProps` 41 | 42 | 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/issues/61). 43 | 44 | **Function Components** 45 | 46 | ```tsx 47 | // using typeof as a shortcut; note that it hoists! 48 | // you can also declare the type of DefaultProps if you choose 49 | // e.g. https://github.com/typescript-cheatsheets/react/issues/415#issuecomment-841223219 50 | type GreetProps = { age: number } & typeof defaultProps; 51 | 52 | const defaultProps = { 53 | age: 21, 54 | }; 55 | 56 | const Greet = (props: GreetProps) => { 57 | // etc 58 | }; 59 | Greet.defaultProps = defaultProps; 60 | ``` 61 | 62 | _[See this in TS Playground](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAOKVYwAKxY6ALxwA3igDmWAFxwAdgFcQAIyxQ4AXzgAyOM1YQCcACZYCyeQBte-VPVwRZqeCbOXrEAXGEi6cCdLgAJgBGABo6dXo6e0d4TixuLzgACjAbGXjuPg9UAEovAD5RXzhKGHkoWTgAHiNgADcCkTScgDpkSTgAeiQFZVVELvVqrrrGiPpMmFaXcytsz2FZtwXbOiA)_ 63 | 64 | For **Class components**, there are [a couple ways to do it](https://github.com/typescript-cheatsheets/react/pull/103#issuecomment-481061483) (including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: 65 | 66 | ```tsx 67 | type GreetProps = typeof Greet.defaultProps & { 68 | age: number; 69 | }; 70 | 71 | class Greet extends React.Component { 72 | static defaultProps = { 73 | age: 21, 74 | }; 75 | /*...*/ 76 | } 77 | 78 | // Type-checks! No type assertions needed! 79 | let el = ; 80 | ``` 81 | 82 |
83 | React.JSX.LibraryManagedAttributes nuance for library authors 84 | 85 | 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`. 86 | 87 | 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 `React.JSX.LibraryManagedAttributes` utility: 88 | 89 | ```tsx 90 | // internal contract, should not be exported out 91 | type GreetProps = { 92 | age: number; 93 | }; 94 | 95 | class Greet extends Component { 96 | static defaultProps = { age: 21 }; 97 | } 98 | 99 | // external contract 100 | export type ApparentGreetProps = React.JSX.LibraryManagedAttributes< 101 | typeof Greet, 102 | GreetProps 103 | >; 104 | ``` 105 | 106 | This will work properly, although hovering over`ApparentGreetProps`may be a little intimidating. You can reduce this boilerplate with the`ComponentProps` utility detailed below. 107 | 108 |
109 | 110 | ## Consuming Props of a Component with defaultProps 111 | 112 | A component with `defaultProps` may seem to have some required props that actually aren't. 113 | 114 | ### Problem Statement 115 | 116 | Here's what you want to do: 117 | 118 | ```tsx 119 | interface IProps { 120 | name: string; 121 | } 122 | const defaultProps = { 123 | age: 25, 124 | }; 125 | const GreetComponent = ({ name, age }: IProps & typeof defaultProps) => ( 126 |
{`Hello, my name is ${name}, ${age}`}
127 | ); 128 | GreetComponent.defaultProps = defaultProps; 129 | 130 | const TestComponent = (props: React.ComponentProps) => { 131 | return

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

; 151 | }; 152 | 153 | // No error 154 | const el = ; 155 | ``` 156 | 157 | [_See this in TS Playground_](https://www.typescriptlang.org/play?#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdMAnmFnAMImQB2W3MABWJhUAHgAqAPjgBeOOLhYAHjD4ATdNjwwAdJ3ARe-cSyyjg3AlihwB0gD6Yqu-Tz4xzl67cl04cAH44ACkAZQANHQAZYAAjKGQoJgBZZG5kAHMsNQBBGBgoOIBXVTFxABofPzgALjheADdrejoLVSgCPDYASSEIETgAb2r0kCw61AKLDPoAXzpcQ0m4NSxOooAbQWF0OWH-TPG4ACYAVnK6WfpF7mWAcUosGFdDd1k4AApB+uQxysO4LM6r0dnAAGRwZisCAEFZrZCbbb9VAASlk0g+1VEamADUkgwABgAJLAbDYQSogJg-MZwYDoAAkg1GWFmlSZh1mBNmogA9Di8XQUfQHlgni8jLpVustn0BnJpQjZTsWrzeXANsh2gwbstxFhJhK3nIPmAdnUjfw5WIoVgYXBReKuK9+JI0TJpPs4JQYEUoNw4KIABYARjgvN8VwYargADkIIooMQoAslvBSe8JAbns7JTSsDIyAQIBAyOHJDQgA) 158 | 159 | ## Misc Discussions and Knowledge 160 | 161 |
162 | Why does React.FC break defaultProps? 163 | 164 | You can check the discussions here: 165 | 166 | - https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 167 | - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 168 | - https://github.com/typescript-cheatsheets/react/issues/87 169 | 170 | This is just the current state and may be fixed in future. 171 | 172 |
173 | 174 |
175 | TypeScript 2.9 and earlier 176 | 177 | 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: 178 | 179 | ```ts 180 | type Props = Required & { 181 | /* additional props here */ 182 | }; 183 | 184 | export class MyComponent extends React.Component { 185 | static defaultProps = { 186 | foo: "foo", 187 | }; 188 | } 189 | ``` 190 | 191 | 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! 192 | 193 | ```ts 194 | interface IMyComponentProps { 195 | firstProp?: string; 196 | secondProp: IPerson[]; 197 | } 198 | 199 | export class MyComponent extends React.Component { 200 | public static defaultProps: Partial = { 201 | firstProp: "default", 202 | }; 203 | } 204 | ``` 205 | 206 | The problem with this approach is it causes complex issues with the type inference working with `React.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. 207 | 208 | [See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react/issues/57) and [here](https://github.com/typescript-cheatsheets/react/issues/61). 209 | 210 |
211 | 212 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 213 | -------------------------------------------------------------------------------- /website/static/img/undraw_tweetstorm.svg: -------------------------------------------------------------------------------- 1 | tweetstorm 2 | -------------------------------------------------------------------------------- /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 MyComponentProps { 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/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 | interface MyComponentProps { 35 | /** Description of prop "label". 36 | * @default foobar 37 | * */ 38 | label?: string; 39 | } 40 | 41 | /** 42 | * General component description in JSDoc format. Markdown is *supported*. 43 | */ 44 | export default function MyComponent({ label = "foobar" }: MyComponentProps) { 45 | return
Hello world {label}
; 46 | } 47 | ``` 48 | 49 | [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) 50 | 51 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 52 | 53 | ## Namespaced Components 54 | 55 | 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()`; 56 | 57 | ```tsx 58 | import { forwardRef } from "react"; 59 | 60 | const Input = (props: any) => ; 61 | 62 | const Form = forwardRef( 63 | ({ children, ...otherProps }, ref) => ( 64 |
65 | {children} 66 |
67 | ) 68 | ); 69 | 70 | /** 71 | * Exported components now can be used as `
` and `` 72 | */ 73 | export default Object.assign(Form, { Input: Input }); 74 | ``` 75 | 76 | [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) 77 | 78 | (Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react/issues/165)) 79 | 80 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 81 | 82 | ## Design System Development 83 | 84 | 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) 85 | 86 | For developing with Storybook, read the docs I wrote over here: . This includes automatic proptype documentation generation, which is awesome :) 87 | 88 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 89 | 90 | ## Migrating From Flow 91 | 92 | You should check out large projects that are migrating from flow to pick up concerns and tips: 93 | 94 | - [Jest](https://github.com/facebook/jest/pull/7554) 95 | - [Expo](https://github.com/expo/expo/issues/2164) 96 | - [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982) 97 | - [Storybook](https://github.com/storybooks/storybook/issues/5030) 98 | - [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf) 99 | 100 | Useful libraries: 101 | 102 | - 103 | - 104 | - 105 | 106 | If you have specific advice in this area, please file a PR! 107 | 108 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 109 | 110 | ## Prettier 111 | 112 | There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! 113 | 114 | ```bash 115 | $ yarn add -D prettier husky lint-staged 116 | ``` 117 | 118 | ```json 119 | // inside package.json 120 | { 121 | //... 122 | "husky": { 123 | "hooks": { 124 | "pre-commit": "lint-staged" 125 | } 126 | }, 127 | "lint-staged": { 128 | "linters": { 129 | "src/*.{ts,tsx,js,jsx,css,scss,md}": [ 130 | "prettier --trailing-comma es5 --single-quote --write", 131 | "git add" 132 | ], 133 | "ignore": ["**/dist/*, **/node_modules/*"] 134 | } 135 | }, 136 | "prettier": { 137 | "printWidth": 80, 138 | "semi": false, 139 | "singleQuote": true, 140 | "trailingComma": "es5" 141 | } 142 | } 143 | ``` 144 | 145 | 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) 146 | 147 | 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. 148 | 149 | ## Testing 150 | 151 | Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions: 152 | 153 | - https://github.com/azz/jest-runner-tsc 154 | - https://github.com/SamVerschueren/tsd 155 | - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) 156 | - https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)) 157 | 158 | ## Working with Non-TypeScript Libraries (writing your own index.d.ts) 159 | 160 | Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: 161 | 162 | ``` 163 | [ts] 164 | 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. 165 | Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016] 166 | ``` 167 | 168 | So create a `.d.ts` file anywhere in your project with the module definition: 169 | 170 | ```ts 171 | // de-indent.d.ts 172 | declare module "de-indent" { 173 | function deindent(): void; 174 | export = deindent; // default export 175 | } 176 | ``` 177 | 178 |
179 | 180 | Further Discussion 181 | 182 | Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/typescript-cheatsheets/react/issues/8). We have more discussion and examples [in our issue here](https://github.com/typescript-cheatsheets/react/issues/12). 183 | 184 |
185 | 186 | ## Compilation Speed 187 | 188 | Compiling large TS projects can get slow. Here are some tips: 189 | 190 | - We have a dedicated repo tracking TS speed recommendations: https://github.com/typescript-cheatsheets/speed 191 | - Use [TS 3.0 Project references](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_version#typescript-30) 192 | - 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) 193 | - Webpack ([see CRA diff](https://gist.github.com/jaredpalmer/d3016701589f14df8a3572df91a5754b)): 194 | - set `output.pathinfo = false` 195 | - set `optimization.splitChunks`, `optimization.removeAvailableModules`, `optimization.removeEmptyChunks` to `false` 196 | -------------------------------------------------------------------------------- /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 | /** an object with known properties (but could have more at runtime) */ 22 | obj: { 23 | id: string; 24 | title: string; 25 | }; 26 | /** array of objects! (common) */ 27 | objArr: { 28 | id: string; 29 | title: string; 30 | }[]; 31 | /** any non-primitive value - can't access any properties (NOT COMMON but useful as placeholder) */ 32 | obj2: object; 33 | /** an interface with no required properties - (NOT COMMON, except for things like `React.Component<{}, State>`) */ 34 | obj3: {}; 35 | /** a dict object with any number of properties of the same type */ 36 | dict1: { 37 | [key: string]: MyTypeHere; 38 | }; 39 | dict2: Record; // equivalent to dict1 40 | /** function that doesn't take or return anything (VERY COMMON) */ 41 | onClick: () => void; 42 | /** function with named prop (VERY COMMON) */ 43 | onChange: (id: number) => void; 44 | /** function type syntax that takes an event (VERY COMMON) */ 45 | onChange: (event: React.ChangeEvent) => void; 46 | /** alternative function type syntax that takes an event (VERY COMMON) */ 47 | onClick(event: React.MouseEvent): void; 48 | /** any function as long as you don't invoke it (not recommended) */ 49 | onSomething: Function; 50 | /** an optional prop (VERY COMMON!) */ 51 | optional?: OptionalType; 52 | /** when passing down the state setter function returned by `useState` to a child component. `number` is an example, swap out with whatever the type of your state */ 53 | setState: React.Dispatch>; 54 | }; 55 | ``` 56 | 57 | ### `object` as the non-primitive type 58 | 59 | `object` is a common source of misunderstanding in TypeScript. It does not mean "any object" but rather "any non-primitive type", which means it represents anything that is not `number`, `bigint`, `string`, `boolean`, `symbol`, `null` or `undefined`. 60 | 61 | Typing "any non-primitive value" is most likely not something that you should do much in React, which means you will probably not use `object` much. 62 | 63 | ### Empty interface, `{}` and `Object` 64 | 65 | An empty interface, `{}` and `Object` all represent "any non-nullish value"—not "an empty object" as you might think. [Using these types is a common source of misunderstanding and is not recommended](https://typescript-eslint.io/rules/no-empty-interface/). 66 | 67 | ```ts 68 | interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object` 69 | 70 | let value: AnyNonNullishValue; 71 | 72 | // these are all fine, but might not be expected 73 | value = 1; 74 | value = "foo"; 75 | value = () => alert("foo"); 76 | value = {}; 77 | value = { foo: "bar" }; 78 | 79 | // these are errors 80 | value = undefined; 81 | value = null; 82 | ``` 83 | 84 | ## Useful React Prop Type Examples 85 | 86 | Relevant for components that accept other React components as props. 87 | 88 | ```tsx 89 | export declare interface AppProps { 90 | children?: React.ReactNode; // best, accepts everything React can render 91 | childrenElement: React.JSX.Element; // A single React element 92 | style?: React.CSSProperties; // to pass through style props 93 | onChange?: React.FormEventHandler; // form events! the generic parameter is the type of event.target 94 | // more info: https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#wrappingmirroring 95 | props: Props & React.ComponentPropsWithoutRef<"button">; // to impersonate all the props of a button element and explicitly not forwarding its ref 96 | props2: Props & React.ComponentPropsWithRef; // to impersonate all the props of MyButtonForwardedRef and explicitly forwarding its ref 97 | } 98 | ``` 99 | 100 |
101 | Small React.ReactNode edge case before React 18 102 | 103 | Before the [React 18 type updates](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/56210), this code typechecked but had a runtime error: 104 | 105 | ```tsx 106 | type Props = { 107 | children?: React.ReactNode; 108 | }; 109 | 110 | function Comp({ children }: Props) { 111 | return
{children}
; 112 | } 113 | function App() { 114 | // Before React 18: Runtime error "Objects are not valid as a React child" 115 | // After React 18: Typecheck error "Type '{}' is not assignable to type 'ReactNode'" 116 | return {{}}; 117 | } 118 | ``` 119 | 120 | This is because `ReactNode` includes `ReactFragment` which allowed type `{}` before React 18. 121 | 122 | [Thanks @pomle for raising this.](https://github.com/typescript-cheatsheets/react/issues/357) 123 | 124 |
125 | 126 |
127 | React.JSX.Element vs React.ReactNode? 128 | 129 | Quote [@ferdaber](https://github.com/typescript-cheatsheets/react/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 `React.JSX.Element` interface, but `React.ReactNode` is the set of all possible return values of a component. 130 | 131 | - `React.JSX.Element` -> Return value of `React.createElement` 132 | - `React.ReactNode` -> Return value of a component 133 | 134 |
135 | 136 | [More discussion: Where ReactNode does not overlap with React.JSX.Element](https://github.com/typescript-cheatsheets/react/issues/129) 137 | 138 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 139 | 140 | ## Types or Interfaces? 141 | 142 | You can use either Types or Interfaces to type Props and State, so naturally the question arises - which do you use? 143 | 144 | ### TL;DR 145 | 146 | Use Interface until You Need Type - [orta](https://twitter.com/orta/status/1356129195835973632?s=20). 147 | 148 | ### More Advice 149 | 150 | Here's a helpful rule of thumb: 151 | 152 | - 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. 153 | 154 | - consider using `type` for your React Component Props and State, for consistency and because it is more constrained. 155 | 156 | 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). 157 | 158 | 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). 159 | 160 | > 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) 161 | 162 | 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. 163 | 164 | ### Useful table for Types vs Interfaces 165 | 166 | It's a nuanced topic, don't get too hung up on it. Here's a handy table: 167 | 168 | | Aspect | Type | Interface | 169 | | ----------------------------------------------- | :--: | :-------: | 170 | | Can describe functions | ✅ | ✅ | 171 | | Can describe constructors | ✅ | ✅ | 172 | | Can describe tuples | ✅ | ✅ | 173 | | Interfaces can extend it | ⚠️ | ✅ | 174 | | Classes can extend it | 🚫 | ✅ | 175 | | Classes can implement it (`implements`) | ⚠️ | ✅ | 176 | | Can intersect another one of its kind | ✅ | ⚠️ | 177 | | Can create a union with another one of its kind | ✅ | 🚫 | 178 | | Can be used to create mapped types | ✅ | 🚫 | 179 | | Can be mapped over with mapped types | ✅ | ✅ | 180 | | Expands in error messages and logs | ✅ | 🚫 | 181 | | Can be augmented | 🚫 | ✅ | 182 | | Can be recursive | ⚠️ | ✅ | 183 | 184 | ⚠️ In some cases 185 | 186 | (source: [Karol Majewski](https://twitter.com/karoljmajewski/status/1082413696075382785)) 187 | 188 | [Something to add? File an issue](https://github.com/typescript-cheatsheets/react/issues/new). 189 | -------------------------------------------------------------------------------- /docs/migration/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: intro 3 | sidebar_label: Intro 4 | title: Migrating (to TypeScript) Cheatsheet 5 | --- 6 | 7 | This Cheatsheet collates advice and utilities from real case studies of teams moving significant codebases from plain JS or Flow over to TypeScript. It makes no attempt to _convince_ people to do so, but we do collect what few statistics companies offer up after their conversion experience. 8 | 9 | > ⚠️ This Cheatsheet is extremely new and could use all the help we can get. Solid advice, results, and up to date content all welcome. 10 | 11 | ## Prerequisite 12 | 13 | Read [TypeScript's official Guide for migrating from JS](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) and you should already be familiar with their [React conversion guide](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide). 14 | 15 | ## General Conversion approaches 16 | 17 | - Level 0: Don't use TypeScript, use JSDoc 18 | - See our [JSDoc section](./js-docs.md) 19 | - Level 1A: Majority JavaScript, increasingly strict TypeScript 20 | - as recommended by [official TS guide](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) 21 | - use `allowJS` (Experiences: [clayallsop][clayallsop], [pleo][pleo]) 22 | - Level 1B: Total rename to TypeScript from the start 23 | - "[Just rename all .js files to .ts](https://twitter.com/jamonholmgren/status/1089241726303199232)"? 24 | - use the loosest, bare minimum settings to start with 25 | - Level 2: Strict TypeScript 26 | - use Microsoft's [`dts-gen`](https://github.com/microsoft/DefinitelyTyped-tools/tree/main/packages/dts-gen) to generate `.d.ts` files for your untyped files. [This SO answer](https://stackoverflow.com/questions/12687779/how-do-you-produce-a-d-ts-typings-definition-file-from-an-existing-javascript) has more on the topic. 27 | - use `declare` keyword for ambient declarations - see [declaration merging](https://github.com/typescript-cheatsheets/react#troubleshooting-handbook-bugs-in-official-typings) to patch library declarations inline 28 | 29 | Misc tips/approaches successful companies have taken 30 | 31 | - `@ts-expect-error` on compiler errors for libraries with no typedefs 32 | - pick ESLint over TSLint (source: [ESLint](https://eslint.org/blog/2019/01/future-typescript-eslint) and [TS Roadmap](https://github.com/Microsoft/TypeScript/issues/29288)). [You can convert TSlint to ESlint with this tool](https://github.com/typescript-eslint/tslint-to-eslint-config). 33 | - New code must always be written in TypeScript. No exceptions. For existing code: If your task requires you to change JavaScript code, you need to rewrite it. (Source: [Hootsuite][hootsuite]) 34 | 35 |
36 | 37 | 38 | Webpack tips 39 | 40 | 41 | 42 | - webpack loader: `awesome-typescript-loader` vs `ts-loader`? (there is some disagreement in community about this - but read [awesome's point of view](https://github.com/s-panferov/awesome-typescript-loader#differences-between-ts-loader)) 43 | - Webpack config: 44 | 45 | ```js 46 | module.exports = { 47 | 48 | resolve: { 49 | - extensions: ['.js', '.jsx'] 50 | + extensions: ['.ts', '.tsx', '.js', '.jsx'] 51 | }, 52 | 53 | // Source maps support ('inline-source-map' also works) 54 | devtool: 'source-map', 55 | 56 | // Add the loader for .ts files. 57 | module: { 58 | loaders: [{ 59 | - test: /\.jsx?$/, 60 | - loader: 'babel-loader', 61 | - exclude: [/node_modules/], 62 | + test: /\.(t|j)sx?$/, 63 | + loader: ['awesome-typescript-loader?module=es6'], 64 | + exclude: [/node_modules/] 65 | + }, { 66 | + test: /\.js$/, 67 | + loader: 'source-map-loader', 68 | + enforce: 'pre' 69 | }] 70 | } 71 | }; 72 | ``` 73 | 74 | Special note on `ts-loader` and 3rd party libraries: https://twitter.com/acemarke/status/1091150384184229888 75 | 76 |
77 | 78 | ## Academic Studies of Migration 79 | 80 | > Note: Empirical Software Engineering is desirable, but [extremely hard](https://hillelwayne.com/talks/what-we-know-we-dont-know/). Please be gentle but also please share if you find quality research! 81 | 82 | - [To Type or Not to Type: Quantifying Detectable Bugs in JavaScript](http://earlbarr.com/publications/typestudy.pdf) 83 | 84 | > Our central finding is that both static type systems find an important percentage of public bugs: both Flow 0.30 and TypeScript 2.0 successfully detect 15%! 85 | 86 | - [Empirical study on the impact of static typing](https://www.researchgate.net/publication/259634489_An_empirical_study_on_the_impact_of_static_typing_on_software_maintainability) 87 | 88 | see also [Things I was Wrong About: Types](https://v5.chriskrycho.com/journal/things-i-was-wrong-about/1-types/) and [A Large Scale Study of Programming Languages 89 | and Code Quality in GitHub](https://web.cs.ucdavis.edu/~filkov/papers/lang_github.pdf) 90 | 91 | ## Misc migration stories by notable companies and open source 92 | 93 | - (2022) Stripe: https://stripe.com/blog/migrating-to-typescript ([podcast](https://devtools.fm/episode/33), [tweet](https://twitter.com/alunny/status/1501261144341680130)) 94 | - [Bloomberg](https://www.techatbloomberg.com/blog/10-insights-adopting-typescript-at-scale/) - [Podcast form](https://talkscript.sitepen.com/episode-57-typescript-at-scale/) 95 | - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) 96 | - Airtable's [Big Bang Migration from Flow to TS](https://medium.com/airtable-eng/the-continual-evolution-of-airtables-codebase-migrating-a-million-lines-of-code-to-typescript-612c008baf5c) 97 | - [Lyft](https://eng.lyft.com/typescript-at-lyft-64f0702346ea) 98 | - [Google](http://neugierig.org/software/blog/2018/09/typescript-at-google.html) 99 | - [Tiny][tiny] - [Talk from ForwardJS here](https://www.slideshare.net/tiny/porting-100k-lines-of-code-to-typescript) 100 | - [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) ([podcast](https://softwareengineeringdaily.com/2017/08/11/typescript-at-slack-with-felix-rieseberg/)) 101 | - [Etsy](https://codeascraft.com/2021/11/08/etsys-journey-to-typescript/) 102 | - [Netflix adoption story](https://www.youtube.com/watch?v=p5Hwb1YbNMY&feature=share) 103 | - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) 104 | - Dropbox 105 | - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) 106 | - [Blogpost: The Great CoffeeScript to TypeScript Migration of 2017](https://dropbox.tech/frontend/the-great-coffeescript-to-typescript-migration-of-2017) 107 | - [Heap - How we failed, then succeeded, at migrating to TypeScript](https://heap.io/blog/migrating-to-typescript) 108 | - Execute Program (Gary Bernhardt) https://www.executeprogram.com/blog/porting-to-typescript-solved-our-api-woes 109 | 110 | Open Source 111 | 112 | - [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) 113 | - [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) 114 | - [Google Workbox migration](https://github.com/GoogleChrome/workbox/pull/2058) 115 | - [Chrome Dev Tools related issues](https://twitter.com/TimvdLippe/status/1220393069792694281) 116 | - [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) 117 | - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) 118 | - [React Native CLI](https://github.com/react-native-community/cli/issues/683) 119 | - [Next.js](https://nextjs.org/blog/next-9) 120 | - React Router 121 | - [v5 attempt](https://github.com/ReactTraining/react-router/issues/6955) 122 | - [React Router v6](https://github.com/ReactTraining/react-router/releases/tag/v6.0.0-alpha.4) 123 | - [history](https://github.com/ReactTraining/history/pull/774) - [final](https://github.com/ReactTraining/history/commit/1e91a64a858604062d804e4d51eb1d2a020a95c8) 124 | - [Docusaurus v2](https://github.com/facebook/docusaurus/pull/2578) 125 | - [Gatsby](https://github.com/gatsbyjs/gatsby/issues/21995) 126 | - [Redux](https://github.com/reduxjs/redux/pull/3536) 127 | - [Theme-UI](https://github.com/system-ui/theme-ui/issues/668) 128 | - [Hasura Console](https://github.com/hasura/graphql-engine/issues/4314) 129 | - [Storybook](https://github.com/storybookjs/storybook/pulls?page=4&q=is%3Apr+sort%3Aupdated-desc+is%3Aclosed+typescript+label%3Atypescript) 130 | - [Dojo 1 -> 2 migration](https://topenddevs.com/podcasts/javascript-jabber/episodes/jsj-277-dojo-2-with-dylan-schiemann-and-kitson-kelly) 131 | 132 | ## Migration Results 133 | 134 | - Number of production deploys doubled for [Hootsuite][hootsuite] 135 | - Found accidental globals for [Tiny][tiny] 136 | - Found incorrect function calls for [Tiny][tiny] 137 | - Found rarely used, buggy code that was untested for [Tiny][tiny] 138 | 139 | [clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 "Incrementally Migrating JavaScript to TypeScript" 140 | [pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 "Migrating a Babel project to TypeScript" 141 | [tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ "Benefits of gradual strong typing in JavaScript" 142 | [entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" 143 | [mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide "TypeScript React Conversion Guide" 144 | [coherentlabs]: https://hashnode.com/post/how-we-migrated-a-200k-loc-project-to-typescript-and-survived-to-tell-the-story-ciyzhikcc0001y253w00n11yb "How we migrated a 200K+ LOC project to TypeScript and survived to tell the story" 145 | [hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" 146 | -------------------------------------------------------------------------------- /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 |