From f5550fc795a5139e9c773a0e01134de213b0dcd6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 2 Apr 2021 14:21:24 +0000 Subject: [PATCH 01/17] Updated README on 2021-04-02T14:21:23.928Z --- README.md | 68 ++++++++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 39 deletions(-) diff --git a/README.md b/README.md index 2a5a2d56..cb80c747 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ - [Using Partial Types](#using-partial-types) - [The Types I need weren't exported!](#the-types-i-need-werent-exported) - [The Types I need don't exist!](#the-types-i-need-dont-exist) - - [Slapping `any` on everything](#slapping-any-on-everything) - - [Autogenerate types](#autogenerate-types) - - [Typing Exported Hooks](#typing-exported-hooks) - - [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> + * [Slapping `any` on everything](#slapping-any-on-everything) + * [Autogenerate types](#autogenerate-types) + * [Typing Exported Hooks](#typing-exported-hooks) + * [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> - [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) - [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilities) - [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) @@ -177,7 +177,6 @@ </details> <!--START-SECTION:setup--> - # Section 1: Setup TypeScript with React ## Prerequisites @@ -255,14 +254,24 @@ You should also check [the new TypeScript docs for official descriptions between # Section 2: Getting Started <!--START-SECTION:function-components--> - ## Function Components These can be written as normal functions that take a `props` argument and return a JSX element. ```tsx -type AppProps = { message: string }; /* could also use interface */ +// Declaring type of props - see "Typing Component Props" for more examples +type AppProps = { + message: string; +}; /* use `interface` if exporting so that consumers can extend */ + +// Easiest way to declare a Function Component; return type is inferred. const App = ({ message }: AppProps) => <div>{message}</div>; + +// you can choose annotate the return type so an error is raised if you accidentally return some other type +const App = ({ message }: AppProps): JSX.Element => <div>{message}</div>; + +// you can also inline the type declaration; eliminates naming the prop types, but looks repetitive +const App = ({ message }: { message: string }) => <div>{message}</div>; ``` <details> @@ -297,11 +306,7 @@ const Title: React.FunctionComponent<{ title: string }> = ({ ``` <details> -<summary> - -Using `React.VoidFunctionComponent` or `React.VFC` instead - -</summary> +<summary>Using `React.VoidFunctionComponent` or `React.VFC` instead</summary> As of [@types/react 16.9.48](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/46643), you can also use `React.VoidFunctionComponent` or `React.VFC` type if you want to type `children` explicitly. This is an interim solution until `FunctionComponent` will accept no children by default (planned for `@types/react@^18.0.0`). @@ -377,7 +382,6 @@ const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element; <!--END-SECTION:function-components--> <!--START-SECTION:hooks--> - ## Hooks Hooks are [supported in `@types/react` from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031). @@ -533,7 +537,7 @@ function MyComponent() { </details> -The second option will infer a `RefObject` instead of a `MutableRefObject`. This means there will be a type error ifyou try to assign to `ref2.current`. +The second option will infer a `RefObject` instead of a `MutableRefObject`. This means there will be a type error if you try to assign to `ref2.current`. The third option will make `ref3.current` mutable, and is intended for "instance variables" that you manage yourself. @@ -661,7 +665,6 @@ If you are writing a React Hooks library, don't forget that you should also expo <!--END-SECTION:hooks--> <!--START-SECTION:class-components--> - ## Class Components Within TypeScript, `React.Component` is a generic type (aka `React.Component<PropType, StateType>`), so you want to provide it with (optional) prop and state type parameters: @@ -836,7 +839,6 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:class-components--> <!--START-SECTION:default-props--> - ## You May Not Need `defaultProps` As per [this tweet](https://twitter.com/dan_abramov/status/1133878326358171650), defaultProps will eventually be deprecated. You can check the discussions here: @@ -936,7 +938,7 @@ export type ApparentGreetProps = JSX.LibraryManagedAttributes< >; ``` -``This will work properly, although hovering over`ApparentGreetProps`may be a little intimidating. You can reduce this boilerplate with the`ComponentProps` utility detailed below. +This will work properly, although hovering over`ApparentGreetProps`may be a little intimidating. You can reduce this boilerplate with the`ComponentProps` utility detailed below. </details> @@ -1047,7 +1049,6 @@ The problem with this approach is it causes complex issues with the type inferen <!--END-SECTION:default-props--> <!--START-SECTION:basic-type-examples--> - ## Typing Component Props This is intended as a basic orientation and reference for React developers familiarizing with TypeScript. @@ -1279,7 +1280,6 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:get-derived-state-from-props--> <!--START-SECTION:forms-and-events--> - ## Forms and Events 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): @@ -1381,7 +1381,6 @@ Of course, if you're making any sort of significant form, [you should use Formik <!--END-SECTION:forms-and-events--> <!--START-SECTION:context--> - ## Context ## Basic Example @@ -1664,7 +1663,6 @@ const Consumer = Context.Consumer; <!--END-SECTION:context--> <!--START-SECTION:forward-create-ref--> - ## forwardRef/createRef Check the [Hooks section](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#hooks) for `useRef`. @@ -1727,7 +1725,6 @@ You may also wish to do [Conditional Rendering with `forwardRef`](https://github <!--END-SECTION:forward-create-ref--> <!--START-SECTION:portals--> - ## Portals Using `ReactDOM.createPortal`: @@ -1837,7 +1834,6 @@ This example is based on the [Event Bubbling Through Portal](https://reactjs.org <!--END-SECTION:portals--> <!--START-SECTION:error-boundaries--> - ## Error Boundaries ### Option 1: Using react-error-boundary @@ -1892,7 +1888,6 @@ export default ErrorBoundary; <!--END-SECTION:error-boundaries--> <!--START-SECTION:concurrent--> - ## Concurrent React/React Suspense _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more on React Suspense and Time Slicing. @@ -1902,7 +1897,6 @@ _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more o <!--END-SECTION:concurrent--> <!--START-SECTION:types--> - # Troubleshooting Handbook: Types > ⚠️ Have you read [the TypeScript FAQ](https://github.com/microsoft/TypeScript/wiki/FAQ?) Your answer might be there! @@ -2082,14 +2076,19 @@ In future you can use the `unique` keyword to brand. [See this PR](https://githu Adding two types together can be handy, for example when your component is supposed to mirror the props of a native component like a `button`: ```tsx -export interface Props { +export interface PrimaryButtonProps { label: string; } export const PrimaryButton = ( - props: Props & React.HTMLProps<HTMLButtonElement> // adding my Props together with the @types/react button provided props -) => <Button {...props} />; + props: PrimaryButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement> +) => { + // do custom buttony stuff + return <button {...props}> {props.label} </button>; +}; ``` +_Playground [here](https://www.typescriptlang.org/play?ssl=4&ssc=1&pln=12&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCipAD0ljmADsYkpN0k4AFKUFKAE8AQgFcYMCE14QwAZzgBvCnDgAbFACMkagFxw5MPkwDmAbgoBfanWjw0Uwzz4gBI8ZKZwAvHAAUKnBgOPL6vPxCYhJSMvJwAGSIxDAAdFGeABIAKgCyADIAghJ8muJIcgA82fnpUgCiakggSCwAfBQAlD6tSoEA9H1wACYQcGiihrhwpdFMggYwopiYgUSLUF4VM55KKXvBsnKWPYoH8ika2mqWcBV921KtFuSWQA)_ + You can also use Intersection Types to make reusable subsets of props for similar components: ```tsx @@ -2443,7 +2442,6 @@ For more information on creating type definitions for class components, you can <!--END-SECTION:types--> <!--START-SECTION:operators--> - # Troubleshooting Handbook: Operators - `typeof` and `instanceof`: type query used for refinement @@ -2467,7 +2465,6 @@ Conditional Types are a difficult topic to get around so here are some extra res <!--END-SECTION:operators--> <!--START-SECTION:utilities--> - # Troubleshooting Handbook: Utilities These are all built in, [see source in es5.d.ts](https://github.com/microsoft/TypeScript/blob/2c458c0d1ccb96442bca9ce43aa987fb0becf8a9/src/lib/es5.d.ts#L1401-L1474): @@ -2489,7 +2486,6 @@ These are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty <!--END-SECTION:utilities--> <!--START-SECTION:ts-config--> - # Troubleshooting Handbook: tsconfig.json 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`): @@ -2541,7 +2537,6 @@ Compilation speed grows linearly with size of codebase. For large projects, you <!--END-SECTION:ts-config--> <!--START-SECTION:official-typings-bugs--> - # Troubleshooting Handbook: Fixing bugs in official typings 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`: @@ -2608,7 +2603,6 @@ You can see examples of these included in the built in type declarations in the <!--END-SECTION:official-typings-bugs--> <!--START-SECTION:non-ts-files--> - # Time to Really Learn TypeScript 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. @@ -2621,6 +2615,7 @@ It is worth mentioning some resources to help you get started: - Anders Hejlsberg's overview of TS: https://www.youtube.com/watch?v=ET4kT88JRXs - Marius Schultz: https://blog.mariusschulz.com/series/typescript-evolution with an [Egghead.io course](https://egghead.io/courses/advanced-static-types-in-typescript) - Basarat's Deep Dive: https://basarat.gitbook.io/typescript/ +- Axel Rauschmeyer's [Tackling TypeScript](https://exploringjs.com/tackling-ts/) - 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`) - Learn about [Generics, Conditional types and Mapped types](https://www.youtube.com/watch?v=PJjeHzvi_VQ&feature=youtu.be) - Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) @@ -2629,7 +2624,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:non-ts-files--> <!--START-SECTION:resources--> - # Other React + TypeScript resources - me! <https://twitter.com/swyx> @@ -2663,7 +2657,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:resources--> <!--START-SECTION:editor-integration--> - # Editor Tooling and Integration - VSCode @@ -2688,7 +2681,6 @@ You may also wish to use alternative logos - [jsx-tsx-logos](https://github.com/ <!--END-SECTION:editor-integration--> <!--START-SECTION:linting--> - # Linting > ⚠️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-typescript-cheatsheet/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). @@ -2819,7 +2811,6 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:other-resources--> <!--START-SECTION:talks--> - # Recommended React + TypeScript talks - [Ultimate React Component Patterns with TypeScript](https://www.youtube.com/watch?v=_PBQ3if6Fmg), by Martin Hochel, GeeCon Prague 2018 @@ -2828,7 +2819,6 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:talks--> <!--START-SECTION:learn-ts--> - # Time to Really Learn TypeScript 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. @@ -2841,6 +2831,7 @@ It is worth mentioning some resources to help you get started: - Anders Hejlsberg's overview of TS: https://www.youtube.com/watch?v=ET4kT88JRXs - Marius Schultz: https://blog.mariusschulz.com/series/typescript-evolution with an [Egghead.io course](https://egghead.io/courses/advanced-static-types-in-typescript) - Basarat's Deep Dive: https://basarat.gitbook.io/typescript/ +- Axel Rauschmeyer's [Tackling TypeScript](https://exploringjs.com/tackling-ts/) - 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`) - Learn about [Generics, Conditional types and Mapped types](https://www.youtube.com/watch?v=PJjeHzvi_VQ&feature=youtu.be) - Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) @@ -2849,7 +2840,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:learn-ts--> <!--START-SECTION:examples--> - # Example App - [Create React App TypeScript Todo Example 2020](https://github.com/laststance/create-react-app-typescript-todo-example-2020) From 406b62b0265ed57c2efa5d69a9881c65ed9efb3f Mon Sep 17 00:00:00 2001 From: Denis LE <ledenis@users.noreply.github.com> Date: Sun, 4 Apr 2021 00:48:46 +0200 Subject: [PATCH 02/17] Fix missing parenthesis in troubleshooting types code example (#399) Co-authored-by: swyx <shawnthe1@gmail.com> --- docs/basic/troubleshooting/types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic/troubleshooting/types.md b/docs/basic/troubleshooting/types.md index 0b8af4ca..e928d7bc 100644 --- a/docs/basic/troubleshooting/types.md +++ b/docs/basic/troubleshooting/types.md @@ -138,7 +138,7 @@ You can also assert a property is non-null, when accessing it: ```ts element.parentNode!.removeChild(element) // ! before the period -myFunction(document.getElementById(dialog.id!)! // ! after the property accessing +myFunction(document.getElementById(dialog.id!)!) // ! after the property accessing let userID!: string // definite assignment assertion... be careful! ``` From 0e2a0d9a038db491a0299110c072bee54069b7dd Mon Sep 17 00:00:00 2001 From: sw-yx <shawnthe1@gmail.com> Date: Sun, 4 Apr 2021 07:20:30 +0800 Subject: [PATCH 03/17] format --- README.md | 32 +++++++++++++++++++++++++---- docs/basic/troubleshooting/types.md | 6 +++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index cb80c747..a6056dca 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ - [Using Partial Types](#using-partial-types) - [The Types I need weren't exported!](#the-types-i-need-werent-exported) - [The Types I need don't exist!](#the-types-i-need-dont-exist) - * [Slapping `any` on everything](#slapping-any-on-everything) - * [Autogenerate types](#autogenerate-types) - * [Typing Exported Hooks](#typing-exported-hooks) - * [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> + - [Slapping `any` on everything](#slapping-any-on-everything) + - [Autogenerate types](#autogenerate-types) + - [Typing Exported Hooks](#typing-exported-hooks) + - [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> - [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) - [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilities) - [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) @@ -177,6 +177,7 @@ </details> <!--START-SECTION:setup--> + # Section 1: Setup TypeScript with React ## Prerequisites @@ -254,6 +255,7 @@ You should also check [the new TypeScript docs for official descriptions between # Section 2: Getting Started <!--START-SECTION:function-components--> + ## Function Components These can be written as normal functions that take a `props` argument and return a JSX element. @@ -382,6 +384,7 @@ const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element; <!--END-SECTION:function-components--> <!--START-SECTION:hooks--> + ## Hooks Hooks are [supported in `@types/react` from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031). @@ -665,6 +668,7 @@ If you are writing a React Hooks library, don't forget that you should also expo <!--END-SECTION:hooks--> <!--START-SECTION:class-components--> + ## Class Components Within TypeScript, `React.Component` is a generic type (aka `React.Component<PropType, StateType>`), so you want to provide it with (optional) prop and state type parameters: @@ -839,6 +843,7 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:class-components--> <!--START-SECTION:default-props--> + ## You May Not Need `defaultProps` As per [this tweet](https://twitter.com/dan_abramov/status/1133878326358171650), defaultProps will eventually be deprecated. You can check the discussions here: @@ -1049,6 +1054,7 @@ The problem with this approach is it causes complex issues with the type inferen <!--END-SECTION:default-props--> <!--START-SECTION:basic-type-examples--> + ## Typing Component Props This is intended as a basic orientation and reference for React developers familiarizing with TypeScript. @@ -1280,6 +1286,7 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:get-derived-state-from-props--> <!--START-SECTION:forms-and-events--> + ## Forms and Events 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): @@ -1381,6 +1388,7 @@ Of course, if you're making any sort of significant form, [you should use Formik <!--END-SECTION:forms-and-events--> <!--START-SECTION:context--> + ## Context ## Basic Example @@ -1663,6 +1671,7 @@ const Consumer = Context.Consumer; <!--END-SECTION:context--> <!--START-SECTION:forward-create-ref--> + ## forwardRef/createRef Check the [Hooks section](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#hooks) for `useRef`. @@ -1725,6 +1734,7 @@ You may also wish to do [Conditional Rendering with `forwardRef`](https://github <!--END-SECTION:forward-create-ref--> <!--START-SECTION:portals--> + ## Portals Using `ReactDOM.createPortal`: @@ -1834,6 +1844,7 @@ This example is based on the [Event Bubbling Through Portal](https://reactjs.org <!--END-SECTION:portals--> <!--START-SECTION:error-boundaries--> + ## Error Boundaries ### Option 1: Using react-error-boundary @@ -1888,6 +1899,7 @@ export default ErrorBoundary; <!--END-SECTION:error-boundaries--> <!--START-SECTION:concurrent--> + ## Concurrent React/React Suspense _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more on React Suspense and Time Slicing. @@ -1897,6 +1909,7 @@ _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more o <!--END-SECTION:concurrent--> <!--START-SECTION:types--> + # Troubleshooting Handbook: Types > ⚠️ Have you read [the TypeScript FAQ](https://github.com/microsoft/TypeScript/wiki/FAQ?) Your answer might be there! @@ -2442,6 +2455,7 @@ For more information on creating type definitions for class components, you can <!--END-SECTION:types--> <!--START-SECTION:operators--> + # Troubleshooting Handbook: Operators - `typeof` and `instanceof`: type query used for refinement @@ -2465,6 +2479,7 @@ Conditional Types are a difficult topic to get around so here are some extra res <!--END-SECTION:operators--> <!--START-SECTION:utilities--> + # Troubleshooting Handbook: Utilities These are all built in, [see source in es5.d.ts](https://github.com/microsoft/TypeScript/blob/2c458c0d1ccb96442bca9ce43aa987fb0becf8a9/src/lib/es5.d.ts#L1401-L1474): @@ -2486,6 +2501,7 @@ These are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty <!--END-SECTION:utilities--> <!--START-SECTION:ts-config--> + # Troubleshooting Handbook: tsconfig.json 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`): @@ -2537,6 +2553,7 @@ Compilation speed grows linearly with size of codebase. For large projects, you <!--END-SECTION:ts-config--> <!--START-SECTION:official-typings-bugs--> + # Troubleshooting Handbook: Fixing bugs in official typings 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`: @@ -2603,6 +2620,7 @@ You can see examples of these included in the built in type declarations in the <!--END-SECTION:official-typings-bugs--> <!--START-SECTION:non-ts-files--> + # Time to Really Learn TypeScript 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. @@ -2624,6 +2642,7 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:non-ts-files--> <!--START-SECTION:resources--> + # Other React + TypeScript resources - me! <https://twitter.com/swyx> @@ -2657,6 +2676,7 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:resources--> <!--START-SECTION:editor-integration--> + # Editor Tooling and Integration - VSCode @@ -2681,6 +2701,7 @@ You may also wish to use alternative logos - [jsx-tsx-logos](https://github.com/ <!--END-SECTION:editor-integration--> <!--START-SECTION:linting--> + # Linting > ⚠️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-typescript-cheatsheet/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). @@ -2811,6 +2832,7 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:other-resources--> <!--START-SECTION:talks--> + # Recommended React + TypeScript talks - [Ultimate React Component Patterns with TypeScript](https://www.youtube.com/watch?v=_PBQ3if6Fmg), by Martin Hochel, GeeCon Prague 2018 @@ -2819,6 +2841,7 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:talks--> <!--START-SECTION:learn-ts--> + # Time to Really Learn TypeScript 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. @@ -2840,6 +2863,7 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:learn-ts--> <!--START-SECTION:examples--> + # Example App - [Create React App TypeScript Todo Example 2020](https://github.com/laststance/create-react-app-typescript-todo-example-2020) diff --git a/docs/basic/troubleshooting/types.md b/docs/basic/troubleshooting/types.md index e928d7bc..f46da4c9 100644 --- a/docs/basic/troubleshooting/types.md +++ b/docs/basic/troubleshooting/types.md @@ -137,9 +137,9 @@ Note that you cannot assert your way to anything - basically it is only for refi You can also assert a property is non-null, when accessing it: ```ts -element.parentNode!.removeChild(element) // ! before the period -myFunction(document.getElementById(dialog.id!)!) // ! after the property accessing -let userID!: string // definite assignment assertion... be careful! +element.parentNode!.removeChild(element); // ! before the period +myFunction(document.getElementById(dialog.id!)!); // ! after the property accessing +let userID!: string; // definite assignment assertion... be careful! ``` Of course, try to actually handle the null case instead of asserting :) From e60954732ff6db83f95be0667a5cdcb20adb6fe1 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann <silbermann.sebastian@gmail.com> Date: Mon, 5 Apr 2021 19:22:26 +0200 Subject: [PATCH 04/17] minor improvements to "adding non-standard attributes" (#400) --- docs/advanced/types-react-ap.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/advanced/types-react-ap.md b/docs/advanced/types-react-ap.md index 6ac5dd29..30a98d6d 100644 --- a/docs/advanced/types-react-ap.md +++ b/docs/advanced/types-react-ap.md @@ -130,9 +130,9 @@ Anything not listed above is considered an internal type and not public. If you' ### Adding non-standard attributes The attributes allowed on host components such as `button` or `img` follow the -HTML living standard. New features that are not yet part of specification +[HTML living standard](https://html.spec.whatwg.org/). New features that are not yet part of the living standard or are only implemented by certain browsers will therefore cause a type error. If -you specifically write code for these browsers or polyfill this attributes you can +you specifically write code for these browsers or polyfill these attributes you can use [module augmentation](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation) to still get those components type checked without having to use `any` or `@ts-ignore`. From e606e038531d7fd9bbb635a746ef8dde73780a15 Mon Sep 17 00:00:00 2001 From: Lars <32460418+advename@users.noreply.github.com> Date: Thu, 15 Apr 2021 01:50:25 +0200 Subject: [PATCH 05/17] Added event types and their descriptions (#402) * Added form types and their descriptions I was looking for a complete list of form types and couldn't find any anywhere. I went through the source code and extracted these with adding some description to them, either using MDN's description or some other website explanation. * Apply suggestions from code review Co-authored-by: swyx <shawnthe1@gmail.com> --- .../basic/getting-started/forms-and-events.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/docs/basic/getting-started/forms-and-events.md b/docs/basic/getting-started/forms-and-events.md index 90526c63..023b0604 100644 --- a/docs/basic/getting-started/forms-and-events.md +++ b/docs/basic/getting-started/forms-and-events.md @@ -98,3 +98,42 @@ If you don't quite care about the type of the event, you can just use React.Synt [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGctoRlM4BeRYmAOgFc6kLABQBKClVoM4AMSbs4o9gD4FFOHAA8mJmrhFMbAN7aozJJgC+u2gGVeAIxDAYRoUgBcndDxsBPGjAAFkgwwGgAogBuSAEiynCGuupI3GBE0QEAIuYovAA2MKIA3Elw1PTwMChQAOYh8ilVtfUodHAwvmBIEKyN1XXwAGQJpckgKMB5noZwkSh5vB5wDFDANDVwFiXk6rtwYK10AO7QACbTs-OLnitrG1ulDzu75VJI45PyTQPc7xN53DmCyQRTgAHowe1Okg0ME0ABrOgAQlKr3gBzoxzOX36IVShxOUFOgKuIPBkI6XVhMMRKOe6ghcBCaG4rN0Fis5CUug0p2AkW59M0eRQ9iQeUFe3U4Q+U1GmjWYF4lWhbAARH9Jmq4DQUCAkOrNXltWDJbsNGCRWKJTywXyBTz7Wb1BoreLnbsAAoEs7ueUaRXKqFddUYrFE7W6-Whn0R8Eei1um3PC1Ox38hOBlUhtV0BxOGDaoGLdUAGQgGzWJrNqYzFAtJhAgpEQA) Of course, if you're making any sort of significant form, [you should use Formik](https://jaredpalmer.com/formik) or [React Hook Form](https://react-hook-form.com/), which are written in TypeScript. + +### List of event types + + +| Event Type | Description | +| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- | +| AnimationEvent | CSS Animations. | +| ChangeEvent | Changing the value of `<input>`, `<select>` and `<textarea>` element. | +| ClipboardEvent | Using copy, paste and cut events. | +| CompositionEvent | Events that occur due to the user indirectly entering text (e.g. depending on Browser and PC setup, a popup window may appear with additional characters if you e.g. want to type Japanese on a US Keyboard) | +| DragEvent | Drag and drop interaction with a pointer device (e.g. mouse). | +| FocusEvent | Event that occurs when elements gets or loses focus. | +| FormEvent | Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted. | +| InvalidEvent | Fired when validity restrictions of an input fails (e.g `<input type="number" max="10">` and someone would insert number 20). | +| KeyboardEvent | User interaction with the keyboard. Each event describes a single key interaction. | +| MouseEvent | Events that occur due to the user interacting with a pointing device (e.g. mouse) | +| PointerEvent | Events that occur due to user interaction with a variety pointing of devices such as mouse, pen/stylus, a touchscreen and which also supports multi-touch. Unless you develop for older browsers (IE10 or Safari 12), pointer events are recommended. Extends UIEvent. | +| TouchEvent | Events that occur due to the user interacting with a touch device. Extends UIEvent. | +| TransitionEvent | CSS Transition. Not fully browser supported. Extends UIEvent | +| UIEvent | Base Event for Mouse, Touch and Pointer events. | +| WheelEvent | Scrolling on a mouse wheel or similar input device. (Note: `wheel` event should not be confused with the `scroll` event) | +| SyntheticEvent | The base event for all above events. Should be used when unsure about event type | + +<details> +<summary> + +**What about `InputEvent`?** + +</summary> + +You've probably noticed that there is no `InputEvent`. This is because it is not supported by Typescript as the event itself has no fully browser support and may behave differently in different browsers. You can use `KeyboardEvent` instead. + +Sources: + + - https://github.com/microsoft/TypeScript/issues/29441 + - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent + - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event + +</details> From 0138b77fe5a721dfbdecbd9c20a20701f0aed5db Mon Sep 17 00:00:00 2001 From: Gerrit Birkeland <gerrit@gerritbirkeland.com> Date: Thu, 15 Apr 2021 23:41:59 -0600 Subject: [PATCH 06/17] fix: Remove incorrect usage of "widening" (#405) Using an intersection type here is actually narrowing the type, but the intent here seems to be to introduce type assertions, not talk about the difference between wider and narrower types. --- docs/basic/getting-started/forms-and-events.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic/getting-started/forms-and-events.md b/docs/basic/getting-started/forms-and-events.md index 023b0604..3d4146b0 100644 --- a/docs/basic/getting-started/forms-and-events.md +++ b/docs/basic/getting-started/forms-and-events.md @@ -61,7 +61,7 @@ The first method uses an inferred method signature `(e: React.FormEvent<HTMLInpu **Typing onSubmit, with Uncontrolled components in a Form** -If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use type widening: +If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use a type assertion: ```tsx <form From 8a0ba0605662d3f9a670b9e4c9def7e0caa5e040 Mon Sep 17 00:00:00 2001 From: swyx <shawnthe1@gmail.com> Date: Sat, 17 Apr 2021 01:31:50 +0800 Subject: [PATCH 07/17] add type assertion for useState thanks @priscilaandreani closes https://github.com/typescript-cheatsheets/react/issues/403 --- docs/basic/getting-started/hooks.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/basic/getting-started/hooks.md b/docs/basic/getting-started/hooks.md index 73c83e07..52e30177 100644 --- a/docs/basic/getting-started/hooks.md +++ b/docs/basic/getting-started/hooks.md @@ -7,7 +7,7 @@ Hooks are [supported in `@types/react` from v16.8 up](https://github.com/Definit ## useState -Type inference works very well most of the time: +Type inference works very well for simple values: ```tsx const [val, toggle] = React.useState(false); @@ -22,10 +22,21 @@ However, many hooks are initialized with null-ish default values, and you may wo ```tsx const [user, setUser] = React.useState<IUser | null>(null); +// later... +setUser(newUser); +``` + +You can also use type assertions if a state is initialized soon after setup and always has a value after: + +```tsx +const [user, setUser] = React.useState<IUser>({} as IUser); + // later... setUser(newUser); ``` +This temporarily "lies" to the TypeScript compiler that `{}` is of type `IUser`. You should follow up by setting the `user` state — if you don't, the rest of your code may rely on the fact that `user` is of type `IUser` and that may lead to runtime errors. + ## useReducer You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise TypeScript will infer it. From affcdf825e23f30e5ddf603c9bf2dd387b68afb0 Mon Sep 17 00:00:00 2001 From: sw-yx <shawnthe1@gmail.com> Date: Sat, 17 Apr 2021 01:32:52 +0800 Subject: [PATCH 08/17] format --- .../basic/getting-started/forms-and-events.md | 43 +++++++++---------- docs/basic/getting-started/hooks.md | 2 +- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/docs/basic/getting-started/forms-and-events.md b/docs/basic/getting-started/forms-and-events.md index 3d4146b0..94cc4942 100644 --- a/docs/basic/getting-started/forms-and-events.md +++ b/docs/basic/getting-started/forms-and-events.md @@ -101,25 +101,24 @@ Of course, if you're making any sort of significant form, [you should use Formik ### List of event types - -| Event Type | Description | -| ---------------- | ----------------------------------------------------------------------------------------------------------------------------- | -| AnimationEvent | CSS Animations. | -| ChangeEvent | Changing the value of `<input>`, `<select>` and `<textarea>` element. | -| ClipboardEvent | Using copy, paste and cut events. | -| CompositionEvent | Events that occur due to the user indirectly entering text (e.g. depending on Browser and PC setup, a popup window may appear with additional characters if you e.g. want to type Japanese on a US Keyboard) | -| DragEvent | Drag and drop interaction with a pointer device (e.g. mouse). | -| FocusEvent | Event that occurs when elements gets or loses focus. | -| FormEvent | Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted. | -| InvalidEvent | Fired when validity restrictions of an input fails (e.g `<input type="number" max="10">` and someone would insert number 20). | -| KeyboardEvent | User interaction with the keyboard. Each event describes a single key interaction. | -| MouseEvent | Events that occur due to the user interacting with a pointing device (e.g. mouse) | -| PointerEvent | Events that occur due to user interaction with a variety pointing of devices such as mouse, pen/stylus, a touchscreen and which also supports multi-touch. Unless you develop for older browsers (IE10 or Safari 12), pointer events are recommended. Extends UIEvent. | -| TouchEvent | Events that occur due to the user interacting with a touch device. Extends UIEvent. | -| TransitionEvent | CSS Transition. Not fully browser supported. Extends UIEvent | -| UIEvent | Base Event for Mouse, Touch and Pointer events. | -| WheelEvent | Scrolling on a mouse wheel or similar input device. (Note: `wheel` event should not be confused with the `scroll` event) | -| SyntheticEvent | The base event for all above events. Should be used when unsure about event type | +| Event Type | Description | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| AnimationEvent | CSS Animations. | +| ChangeEvent | Changing the value of `<input>`, `<select>` and `<textarea>` element. | +| ClipboardEvent | Using copy, paste and cut events. | +| CompositionEvent | Events that occur due to the user indirectly entering text (e.g. depending on Browser and PC setup, a popup window may appear with additional characters if you e.g. want to type Japanese on a US Keyboard) | +| DragEvent | Drag and drop interaction with a pointer device (e.g. mouse). | +| FocusEvent | Event that occurs when elements gets or loses focus. | +| FormEvent | Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted. | +| InvalidEvent | Fired when validity restrictions of an input fails (e.g `<input type="number" max="10">` and someone would insert number 20). | +| KeyboardEvent | User interaction with the keyboard. Each event describes a single key interaction. | +| MouseEvent | Events that occur due to the user interacting with a pointing device (e.g. mouse) | +| PointerEvent | Events that occur due to user interaction with a variety pointing of devices such as mouse, pen/stylus, a touchscreen and which also supports multi-touch. Unless you develop for older browsers (IE10 or Safari 12), pointer events are recommended. Extends UIEvent. | +| TouchEvent | Events that occur due to the user interacting with a touch device. Extends UIEvent. | +| TransitionEvent | CSS Transition. Not fully browser supported. Extends UIEvent | +| UIEvent | Base Event for Mouse, Touch and Pointer events. | +| WheelEvent | Scrolling on a mouse wheel or similar input device. (Note: `wheel` event should not be confused with the `scroll` event) | +| SyntheticEvent | The base event for all above events. Should be used when unsure about event type | <details> <summary> @@ -132,8 +131,8 @@ You've probably noticed that there is no `InputEvent`. This is because it is not Sources: - - https://github.com/microsoft/TypeScript/issues/29441 - - https://developer.mozilla.org/en-US/docs/Web/API/InputEvent - - https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event +- https://github.com/microsoft/TypeScript/issues/29441 +- https://developer.mozilla.org/en-US/docs/Web/API/InputEvent +- https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event </details> diff --git a/docs/basic/getting-started/hooks.md b/docs/basic/getting-started/hooks.md index 52e30177..0a6d0f3a 100644 --- a/docs/basic/getting-started/hooks.md +++ b/docs/basic/getting-started/hooks.md @@ -23,7 +23,7 @@ However, many hooks are initialized with null-ish default values, and you may wo const [user, setUser] = React.useState<IUser | null>(null); // later... -setUser(newUser); +setUser(newUser); ``` You can also use type assertions if a state is initialized soon after setup and always has a value after: From fc7273a787536bfbc79dd5688f0d0cbadb79b818 Mon Sep 17 00:00:00 2001 From: Christopher Kumm <40023417+ChrisCrossCrash@users.noreply.github.com> Date: Thu, 22 Apr 2021 11:50:54 +0300 Subject: [PATCH 09/17] Fixed typo (#406) Fixed an issue where a paragraph described three different options as two. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6056dca..211f610d 100644 --- a/README.md +++ b/README.md @@ -510,7 +510,7 @@ function DelayedEffect(props: { timerMs: number }) { ## useRef -When using `useRef`, you have two options when creating a ref container that does not have an initial value: +When using `useRef`, you have three options when creating a ref container that does not have an initial value: ```ts const ref1 = useRef<HTMLElement>(null!); From 314b1b3344a36813416326333b0dd0698aed3d47 Mon Sep 17 00:00:00 2001 From: Rafael Pedicini <rafael@rafgraph.dev> Date: Fri, 23 Apr 2021 17:33:07 -0400 Subject: [PATCH 10/17] Add rollpkg as an option for creating libraries (#407) --- docs/advanced/index.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/advanced/index.md b/docs/advanced/index.md index 870d0563..a6424d6a 100644 --- a/docs/advanced/index.md +++ b/docs/advanced/index.md @@ -14,6 +14,8 @@ title: Advanced Cheatsheet 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. +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. + - Be sure to also check [`basarat`'s guide](https://basarat.gitbooks.io/typescript/content/docs/quick/library.html) for library tsconfig settings. - Alec Larson: [The best Rollup config for TypeScript libraries](https://gist.github.com/aleclarson/9900ed2a9a3119d865286b218e14d226) - From the Angular world, check out https://github.com/bitjson/typescript-starter From b25df79d776aec1f8453952777123ff421172b1b Mon Sep 17 00:00:00 2001 From: Mandy YP <63463742+mmdHasan-yazdanPanah@users.noreply.github.com> Date: Mon, 26 Apr 2021 00:42:17 +0430 Subject: [PATCH 11/17] useLocalStorage Type detection bug (#409) in my vs code it detects that the type of soredValue is (T | (value: T | ((val: T) => T)) => void) but it must be just (T) so it throws error with this change it solved. --- docs/basic/useful-hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic/useful-hooks.md b/docs/basic/useful-hooks.md index dea956a0..8821ffc3 100644 --- a/docs/basic/useful-hooks.md +++ b/docs/basic/useful-hooks.md @@ -37,7 +37,7 @@ function App() { } // Hook -function useLocalStorage<T>(key: string, initialValue: T) { +function useLocalStorage<T>(key: string, initialValue: T) : [T, (value: T | ((val: T) => T)) => void] { // State to store our value // Pass initial state function to useState so logic is only executed once const [storedValue, setStoredValue] = useState<T>(() => { From 21813acec5b2c9baac10fc750fe0966db6c58ad7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 25 Apr 2021 20:13:23 +0000 Subject: [PATCH 12/17] Updated README on 2021-04-25T20:13:23.562Z --- README.md | 93 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 34 deletions(-) diff --git a/README.md b/README.md index 211f610d..8cfb13c5 100644 --- a/README.md +++ b/README.md @@ -158,10 +158,10 @@ - [Using Partial Types](#using-partial-types) - [The Types I need weren't exported!](#the-types-i-need-werent-exported) - [The Types I need don't exist!](#the-types-i-need-dont-exist) - - [Slapping `any` on everything](#slapping-any-on-everything) - - [Autogenerate types](#autogenerate-types) - - [Typing Exported Hooks](#typing-exported-hooks) - - [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> + * [Slapping `any` on everything](#slapping-any-on-everything) + * [Autogenerate types](#autogenerate-types) + * [Typing Exported Hooks](#typing-exported-hooks) + * [Typing Exported Components](#typing-exported-components)<!--END-SECTION:types-toc--> - [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) - [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilities) - [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) @@ -177,7 +177,6 @@ </details> <!--START-SECTION:setup--> - # Section 1: Setup TypeScript with React ## Prerequisites @@ -255,7 +254,6 @@ You should also check [the new TypeScript docs for official descriptions between # Section 2: Getting Started <!--START-SECTION:function-components--> - ## Function Components These can be written as normal functions that take a `props` argument and return a JSX element. @@ -384,14 +382,13 @@ const MyArrayComponent = () => (Array(5).fill(<div />) as any) as JSX.Element; <!--END-SECTION:function-components--> <!--START-SECTION:hooks--> - ## Hooks Hooks are [supported in `@types/react` from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/a05cc538a42243c632f054e42eab483ebf1560ab/types/react/index.d.ts#L800-L1031). ## useState -Type inference works very well most of the time: +Type inference works very well for simple values: ```tsx const [val, toggle] = React.useState(false); @@ -410,6 +407,17 @@ const [user, setUser] = React.useState<IUser | null>(null); setUser(newUser); ``` +You can also use type assertions if a state is initialized soon after setup and always has a value after: + +```tsx +const [user, setUser] = React.useState<IUser>({} as IUser); + +// later... +setUser(newUser); +``` + +This temporarily "lies" to the TypeScript compiler that `{}` is of type `IUser`. You should follow up by setting the `user` state — if you don't, the rest of your code may rely on the fact that `user` is of type `IUser` and that may lead to runtime errors. + ## useReducer You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise TypeScript will infer it. @@ -510,7 +518,7 @@ function DelayedEffect(props: { timerMs: number }) { ## useRef -When using `useRef`, you have three options when creating a ref container that does not have an initial value: +When using `useRef`, you have two options when creating a ref container that does not have an initial value: ```ts const ref1 = useRef<HTMLElement>(null!); @@ -668,7 +676,6 @@ If you are writing a React Hooks library, don't forget that you should also expo <!--END-SECTION:hooks--> <!--START-SECTION:class-components--> - ## Class Components Within TypeScript, `React.Component` is a generic type (aka `React.Component<PropType, StateType>`), so you want to provide it with (optional) prop and state type parameters: @@ -843,7 +850,6 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:class-components--> <!--START-SECTION:default-props--> - ## You May Not Need `defaultProps` As per [this tweet](https://twitter.com/dan_abramov/status/1133878326358171650), defaultProps will eventually be deprecated. You can check the discussions here: @@ -1054,7 +1060,6 @@ The problem with this approach is it causes complex issues with the type inferen <!--END-SECTION:default-props--> <!--START-SECTION:basic-type-examples--> - ## Typing Component Props This is intended as a basic orientation and reference for React developers familiarizing with TypeScript. @@ -1286,7 +1291,6 @@ class Comp extends React.PureComponent<Props, State> { <!--END-SECTION:get-derived-state-from-props--> <!--START-SECTION:forms-and-events--> - ## Forms and Events 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): @@ -1347,7 +1351,7 @@ The first method uses an inferred method signature `(e: React.FormEvent<HTMLInpu **Typing onSubmit, with Uncontrolled components in a Form** -If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use type widening: +If you don't quite care about the type of the event, you can just use React.SyntheticEvent. If your target form has custom named inputs that you'd like to access, you can use a type assertion: ```tsx <form @@ -1385,10 +1389,47 @@ If you don't quite care about the type of the event, you can just use React.Synt Of course, if you're making any sort of significant form, [you should use Formik](https://jaredpalmer.com/formik) or [React Hook Form](https://react-hook-form.com/), which are written in TypeScript. +### List of event types + +| Event Type | Description | +| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| AnimationEvent | CSS Animations. | +| ChangeEvent | Changing the value of `<input>`, `<select>` and `<textarea>` element. | +| ClipboardEvent | Using copy, paste and cut events. | +| CompositionEvent | Events that occur due to the user indirectly entering text (e.g. depending on Browser and PC setup, a popup window may appear with additional characters if you e.g. want to type Japanese on a US Keyboard) | +| DragEvent | Drag and drop interaction with a pointer device (e.g. mouse). | +| FocusEvent | Event that occurs when elements gets or loses focus. | +| FormEvent | Event that occurs whenever a form or form element gets/loses focus, a form element value is changed or the form is submitted. | +| InvalidEvent | Fired when validity restrictions of an input fails (e.g `<input type="number" max="10">` and someone would insert number 20). | +| KeyboardEvent | User interaction with the keyboard. Each event describes a single key interaction. | +| MouseEvent | Events that occur due to the user interacting with a pointing device (e.g. mouse) | +| PointerEvent | Events that occur due to user interaction with a variety pointing of devices such as mouse, pen/stylus, a touchscreen and which also supports multi-touch. Unless you develop for older browsers (IE10 or Safari 12), pointer events are recommended. Extends UIEvent. | +| TouchEvent | Events that occur due to the user interacting with a touch device. Extends UIEvent. | +| TransitionEvent | CSS Transition. Not fully browser supported. Extends UIEvent | +| UIEvent | Base Event for Mouse, Touch and Pointer events. | +| WheelEvent | Scrolling on a mouse wheel or similar input device. (Note: `wheel` event should not be confused with the `scroll` event) | +| SyntheticEvent | The base event for all above events. Should be used when unsure about event type | + +<details> +<summary> + +**What about `InputEvent`?** + +</summary> + +You've probably noticed that there is no `InputEvent`. This is because it is not supported by Typescript as the event itself has no fully browser support and may behave differently in different browsers. You can use `KeyboardEvent` instead. + +Sources: + +- https://github.com/microsoft/TypeScript/issues/29441 +- https://developer.mozilla.org/en-US/docs/Web/API/InputEvent +- https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/input_event + +</details> + <!--END-SECTION:forms-and-events--> <!--START-SECTION:context--> - ## Context ## Basic Example @@ -1671,7 +1712,6 @@ const Consumer = Context.Consumer; <!--END-SECTION:context--> <!--START-SECTION:forward-create-ref--> - ## forwardRef/createRef Check the [Hooks section](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#hooks) for `useRef`. @@ -1734,7 +1774,6 @@ You may also wish to do [Conditional Rendering with `forwardRef`](https://github <!--END-SECTION:forward-create-ref--> <!--START-SECTION:portals--> - ## Portals Using `ReactDOM.createPortal`: @@ -1844,7 +1883,6 @@ This example is based on the [Event Bubbling Through Portal](https://reactjs.org <!--END-SECTION:portals--> <!--START-SECTION:error-boundaries--> - ## Error Boundaries ### Option 1: Using react-error-boundary @@ -1899,7 +1937,6 @@ export default ErrorBoundary; <!--END-SECTION:error-boundaries--> <!--START-SECTION:concurrent--> - ## Concurrent React/React Suspense _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more on React Suspense and Time Slicing. @@ -1909,7 +1946,6 @@ _Not written yet._ watch <https://github.com/sw-yx/fresh-async-react> for more o <!--END-SECTION:concurrent--> <!--START-SECTION:types--> - # Troubleshooting Handbook: Types > ⚠️ Have you read [the TypeScript FAQ](https://github.com/microsoft/TypeScript/wiki/FAQ?) Your answer might be there! @@ -2045,9 +2081,9 @@ Note that you cannot assert your way to anything - basically it is only for refi You can also assert a property is non-null, when accessing it: ```ts -element.parentNode!.removeChild(element) // ! before the period -myFunction(document.getElementById(dialog.id!)! // ! after the property accessing -let userID!: string // definite assignment assertion... be careful! +element.parentNode!.removeChild(element); // ! before the period +myFunction(document.getElementById(dialog.id!)!); // ! after the property accessing +let userID!: string; // definite assignment assertion... be careful! ``` Of course, try to actually handle the null case instead of asserting :) @@ -2455,7 +2491,6 @@ For more information on creating type definitions for class components, you can <!--END-SECTION:types--> <!--START-SECTION:operators--> - # Troubleshooting Handbook: Operators - `typeof` and `instanceof`: type query used for refinement @@ -2479,7 +2514,6 @@ Conditional Types are a difficult topic to get around so here are some extra res <!--END-SECTION:operators--> <!--START-SECTION:utilities--> - # Troubleshooting Handbook: Utilities These are all built in, [see source in es5.d.ts](https://github.com/microsoft/TypeScript/blob/2c458c0d1ccb96442bca9ce43aa987fb0becf8a9/src/lib/es5.d.ts#L1401-L1474): @@ -2501,7 +2535,6 @@ These are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty <!--END-SECTION:utilities--> <!--START-SECTION:ts-config--> - # Troubleshooting Handbook: tsconfig.json 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`): @@ -2553,7 +2586,6 @@ Compilation speed grows linearly with size of codebase. For large projects, you <!--END-SECTION:ts-config--> <!--START-SECTION:official-typings-bugs--> - # Troubleshooting Handbook: Fixing bugs in official typings 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`: @@ -2620,7 +2652,6 @@ You can see examples of these included in the built in type declarations in the <!--END-SECTION:official-typings-bugs--> <!--START-SECTION:non-ts-files--> - # Time to Really Learn TypeScript 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. @@ -2642,7 +2673,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:non-ts-files--> <!--START-SECTION:resources--> - # Other React + TypeScript resources - me! <https://twitter.com/swyx> @@ -2676,7 +2706,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:resources--> <!--START-SECTION:editor-integration--> - # Editor Tooling and Integration - VSCode @@ -2701,7 +2730,6 @@ You may also wish to use alternative logos - [jsx-tsx-logos](https://github.com/ <!--END-SECTION:editor-integration--> <!--START-SECTION:linting--> - # Linting > ⚠️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-typescript-cheatsheet/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). @@ -2832,7 +2860,6 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:other-resources--> <!--START-SECTION:talks--> - # Recommended React + TypeScript talks - [Ultimate React Component Patterns with TypeScript](https://www.youtube.com/watch?v=_PBQ3if6Fmg), by Martin Hochel, GeeCon Prague 2018 @@ -2841,7 +2868,6 @@ If you're looking for information on Prettier, check out the [Prettier](https:// <!--END-SECTION:talks--> <!--START-SECTION:learn-ts--> - # Time to Really Learn TypeScript 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. @@ -2863,7 +2889,6 @@ It is worth mentioning some resources to help you get started: <!--END-SECTION:learn-ts--> <!--START-SECTION:examples--> - # Example App - [Create React App TypeScript Todo Example 2020](https://github.com/laststance/create-react-app-typescript-todo-example-2020) From 09b10d42293225a2027ea5ac4edfc5d322f995bb Mon Sep 17 00:00:00 2001 From: Thien Do <hello@thien.do> Date: Tue, 4 May 2021 05:22:17 +0700 Subject: [PATCH 13/17] Clarify useRef hook usages (#412) Co-authored-by: swyx <shawnthe1@gmail.com> --- docs/basic/getting-started/hooks.md | 101 ++++++++++++++++------------ 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/docs/basic/getting-started/hooks.md b/docs/basic/getting-started/hooks.md index 0a6d0f3a..29ecc4d2 100644 --- a/docs/basic/getting-started/hooks.md +++ b/docs/basic/getting-started/hooks.md @@ -137,65 +137,82 @@ function DelayedEffect(props: { timerMs: number }) { ## useRef -When using `useRef`, you have two options when creating a ref container that does not have an initial value: +In TypeScript, `useRef` returns a reference that is either [read-only](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1025-L1039) or [mutable](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/abd69803c1b710db58d511f4544ec1b70bc9077c/types/react/v16/index.d.ts#L1012-L1023), depends on whether your type argument fully covers the initial value or not. Choose one that suits your use case. -```ts -const ref1 = useRef<HTMLElement>(null!); -const ref2 = useRef<HTMLElement>(null); -const ref3 = useRef<HTMLElement | null>(null); -``` +### Option 1: DOM element ref -You can see the difference in [this playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwCwAUI7hAHarwCCYYcAvHAAUASn4A+OAG9GjOHAD0CBLLnKGcxHABiwKBzgQwMYGxS4WUACbBWAczgwIcSxFwBXEFlYxkxtgDoVTQBJVmBjZAAbOAA3KLcsOAB3YEjogCNE1jc0-zgAGQBPG3tHOAAVQrAsAGVcKGAjOHTCuDdUErhWNgBabLSUVFQsWBNWA2qoX2hA9VU4AGFKXyx0AFk3H3TIxOwCOAB5dIArLHwgpHcoSm84MGJJmFbgdG74ZcsDVkjC2Y01f7yFQsdjvLAEACM-EwVBg-naWD2AB4ABLlNb5GpgZCsACiO083jEgn6kQAhMJ6HMQfpKJCFpE2IkBNg8HCEci0RisTj8VhCTBiaSKVSVIoAaoLnBQuFgFFYvFEikBpkujkMps4FgAB7VfCdLmY7F4gleOFwAByEHg7U63VYfXVg2Go1MhhG0ygf3mAHVUtF6jgYLtwUdTvguta4Bstjs9mGznCpVcbvB7u7YM90B8vj9vYgLkDqWxaeCAEzQ1n4eHDTnoo2801EknqykyObii5SmpnNifA5GMZmCzWOwOJwudwC3xjKUyiLROKRBLJf3NLJO9KanV64xj0koVifQ08k38s1Sv0DJZBxIx5DbRGhk6J5Nua5mu4PEZPOAvSNgsgnxsHmXZzIgRZyDSYIEAAzJWsI1k+BCovWp58gKcAAD5qmkQqtqKHbyCexoYRecw7IQugcAs76ptCdIQv4KZmoRcjyMRaGkU28A4aSKiUXAwwgpYtEfrcAh0mWzF0ax7bsZx3Lceetx8eqAlYPAMAABa6KJskSXAdKwTJ4kwGxCjyKy-bfK05SrDA8mWVagHAbZeScOY0CjqUE6uOgqDaRAOSfKqOYgb8KiMaZ9GSeCEIMkyMVyUwRHWYc7nSvAgUQEk6AjMQXpReWyWGdFLHeBZHEuTCQEZT8xVwaV8BxZCzUWZQMDvuMghBHASJVnCWhTLYApiH1chIqgxpGeCfCSIxAC+Yj3o+8YvvgSLyNNOLjeBGhTTNdLzVJy3reGMBbTtrB7RoB3XbNBAneCsHLatcbPhdV3GrdB1WYhw3IKNZq-W2DCLYRO7QPAljgsgORcDwVJAA), thanks to [this discussion with @rajivpunjabi](https://github.com/typescript-cheatsheets/react/issues/388). +**[To access a DOM element](https://reactjs.org/docs/refs-and-the-dom.html):** provide only the element type as argument, and use `null` as initial value. In this case, the returned reference will have a read-only `.current` that is managed by React. TypeScript expects you to give this ref to an element's `ref` prop: -The first option will bypass nullchecks on `ref1.current`, and is intended to be passed in to built-in `ref` attributes that React will manage (because React handles setting the `current` value for you). - -<details> - <summary>What is the <code>!</code> at the end of <code>null!</code>?</summary> - -`null!` is a non-null assertion operator (the `!`). It asserts that any expression before it is not `null` or `undefined`, so if you have `useRef<HTMLElement>(null!)` it means that you're instantiating the ref with a current value of `null` but lying to TypeScript that it's not `null`. - -```ts -function MyComponent() { - const ref1 = useRef<HTMLDivElement>(null!); +```tsx +function Foo() { + // - If possible, prefer as specific as possible. For example, HTMLDivElement + // is better than HTMLElement and way better than Element. + // - Technical-wise, this returns RefObject<HTMLDivElement> + const divRef = useRef<HTMLDivElement>(null); + useEffect(() => { - doSomethingWith(ref1.current); - // TypeScript won't require null-check e.g. ref1 && ref1.current + // Note that ref.current may be null. This is expected, because you may + // conditionally render the ref-ed element, or you may forgot to assign it + if (divRef.current) throw Error("divRef is not assigned"); + + // Now divRef.current is sure to be HTMLDivElement + doSomethingWith(divRef.current); }); - return <div ref={ref1}> etc </div>; + + // Give the ref to an element so React can manage it for you + return <div ref={divRef}>etc</div>; } ``` +If you are sure that `divRef.current` will never be null, it is also possible to use the non-null assertion operator `!`: + +``` +const divRef = useRef<HTMLDivElement>(null!); +// Later... No need to check if it is null +doSomethingWith(divRef.current); +``` + +Note that you are opting out of type safety here - you will have a runtime error if you forget to assign the ref to an element in the render, or if the ref-ed element is conditionally rendered. + +<details> +<summary> + + Tip: Choosing which `HTMLElement` to use + +</summary> + + +Refs demand specificity - it is not enough to just specify any old `HTMLElement`. If you don't know the name of the element type you need, you can check [lib.dom.ts](https://github.com/microsoft/TypeScript/blob/v3.9.5/lib/lib.dom.d.ts#L19224-L19343) or make an intentional type error and let the language service tell you: + + + </details> -The second option will infer a `RefObject` instead of a `MutableRefObject`. This means there will be a type error if you try to assign to `ref2.current`. -The third option will make `ref3.current` mutable, and is intended for "instance variables" that you manage yourself. +### Option 2: Mutable value ref + +**[To have a mutable value](https://reactjs.org/docs/hooks-faq.html#is-there-something-like-instance-variables):** provide the type you want, and make sure the initial value fully belongs to that type: ```tsx -function TextInputWithFocusButton() { - // initialise with null, but tell TypeScript we are looking for an HTMLInputElement - const inputEl = React.useRef<HTMLInputElement>(null); - const onButtonClick = () => { - // strict null checks need us to check if inputEl and current exist. - // but once current exists, it is of type HTMLInputElement, thus it - // has the method focus! ✅ - if (inputEl && inputEl.current) { - inputEl.current.focus(); - } - }; - return ( - <> - {/* in addition, inputEl only can be used with input elements. Yay! */} - <input ref={inputEl} type="text" /> - <button onClick={onButtonClick}>Focus the input</button> - </> - ); +function Foo() { + // Technical-wise, this returns MutableRefObject<number | null> + const intervalRef = useRef<number | null>(null); + + // You manage the ref yourself (that's why it's called MutableRefObject!) + useEffect(() => { + intervalRef.current = setInterval(...); + return () => clearInterval(intervalRef.current); + }, []); + + // The ref is not passed to any element's "ref" prop + return <button onClick={/* clearInterval the ref */}>Cancel timer</button>; } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) +### See also -example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) +- [Related issue by @rajivpunjabi](https://github.com/typescript-cheatsheets/react/issues/388) - [Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwCwAUI7hAHarwCCYYcAvHAAUASn4A+OAG9GjOHAD0CBLLnKGcxHABiwKBzgQwMYGxS4WUACbBWAczgwIcSxFwBXEFlYxkxtgDoVTQBJVmBjZAAbOAA3KLcsOAB3YEjogCNE1jc0-zgAGQBPG3tHOAAVQrAsAGVcKGAjOHTCuDdUErhWNgBabLSUVFQsWBNWA2qoX2hA9VU4AGFKXyx0AFk3H3TIxOwCOAB5dIArLHwgpHcoSm84MGJJmFbgdG74ZcsDVkjC2Y01f7yFQsdjvLAEACM-EwVBg-naWD2AB4ABLlNb5GpgZCsACiO083jEgn6kQAhMJ6HMQfpKJCFpE2IkBNg8HCEci0RisTj8VhCTBiaSKVSVIoAaoLnBQuFgFFYvFEikBpkujkMps4FgAB7VfCdLmY7F4gleOFwAByEHg7U63VYfXVg2Go1MhhG0ygf3mAHVUtF6jgYLtwUdTvguta4Bstjs9mGznCpVcbvB7u7YM90B8vj9vYgLkDqWxaeCAEzQ1n4eHDTnoo2801EknqykyObii5SmpnNifA5GMZmCzWOwOJwudwC3xjKUyiLROKRBLJf3NLJO9KanV64xj0koVifQ08k38s1Sv0DJZBxIx5DbRGhk6J5Nua5mu4PEZPOAvSNgsgnxsHmXZzIgRZyDSYIEAAzJWsI1k+BCovWp58gKcAAD5qmkQqtqKHbyCexoYRecw7IQugcAs76ptCdIQv4KZmoRcjyMRaGkU28A4aSKiUXAwwgpYtEfrcAh0mWzF0ax7bsZx3Lceetx8eqAlYPAMAABa6KJskSXAdKwTJ4kwGxCjyKy-bfK05SrDA8mWVagHAbZeScOY0CjqUE6uOgqDaRAOSfKqOYgb8KiMaZ9GSeCEIMkyMVyUwRHWYc7nSvAgUQEk6AjMQXpReWyWGdFLHeBZHEuTCQEZT8xVwaV8BxZCzUWZQMDvuMghBHASJVnCWhTLYApiH1chIqgxpGeCfCSIxAC+Yj3o+8YvvgSLyNNOLjeBGhTTNdLzVJy3reGMBbTtrB7RoB3XbNBAneCsHLatcbPhdV3GrdB1WYhw3IKNZq-W2DCLYRO7QPAljgsgORcDwVJAA) +- [Example from Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) - [Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) ## useImperativeHandle From 937d712150d35d6ea33559cff22edfbf7f8f83c3 Mon Sep 17 00:00:00 2001 From: swyx <shawnthe1@gmail.com> Date: Tue, 4 May 2021 18:09:50 +0800 Subject: [PATCH 14/17] Update index.md --- docs/migration/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/migration/index.md b/docs/migration/index.md index 77ccd599..9d434213 100644 --- a/docs/migration/index.md +++ b/docs/migration/index.md @@ -89,6 +89,7 @@ see also [Things I was Wrong About: Types](https://v5.chriskrycho.com/journal/th - [Bloomberg](https://www.techatbloomberg.com/blog/10-insights-adopting-typescript-at-scale/) - [Podcast form](https://talkscript.sitepen.com/episode-57-typescript-at-scale/) - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) +- 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) - [Lyft](https://eng.lyft.com/typescript-at-lyft-64f0702346ea) - [Google](http://neugierig.org/software/blog/2018/09/typescript-at-google.html) - [Tiny][tiny] - [Talk from ForwardJS here](https://www.slideshare.net/tiny/porting-100k-lines-of-code-to-typescript) From 846474f86b97a73ca9b780325f944459fa484b41 Mon Sep 17 00:00:00 2001 From: swyx <shawnthe1@gmail.com> Date: Tue, 4 May 2021 18:10:49 +0800 Subject: [PATCH 15/17] Update from-flow.md --- docs/migration/from-flow.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/migration/from-flow.md b/docs/migration/from-flow.md index 2ed574d2..501df177 100644 --- a/docs/migration/from-flow.md +++ b/docs/migration/from-flow.md @@ -4,6 +4,7 @@ title: From Flow --- - 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-typescript-cheatsheet/pull/79#issuecomment-458227322) at TravelRepublic) +- 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 - [Incremental Migration to TypeScript on a Flowtype codebase][entria] at Entria - [MemSQL's Studio's migration](https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/) - blogpost with many useful tips - Retail-UI's Codemod: https://github.com/skbkontur/retail-ui/tree/master/packages/react-ui-codemodes/flow-to-ts From efb6291065ceec744b4eb55b82190708a42451e9 Mon Sep 17 00:00:00 2001 From: Christina Grannas <christina.grannas@gmail.com> Date: Wed, 5 May 2021 21:32:23 +0200 Subject: [PATCH 16/17] docs: throw when ref.current is null (#413) --- docs/basic/getting-started/hooks.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/basic/getting-started/hooks.md b/docs/basic/getting-started/hooks.md index 29ecc4d2..d3ca09b8 100644 --- a/docs/basic/getting-started/hooks.md +++ b/docs/basic/getting-started/hooks.md @@ -153,7 +153,7 @@ function Foo() { useEffect(() => { // Note that ref.current may be null. This is expected, because you may // conditionally render the ref-ed element, or you may forgot to assign it - if (divRef.current) throw Error("divRef is not assigned"); + if (!divRef.current) throw Error("divRef is not assigned"); // Now divRef.current is sure to be HTMLDivElement doSomethingWith(divRef.current); From bcd41bfbb6c782af5677d50f6260ba8188dff20f Mon Sep 17 00:00:00 2001 From: swyx <shawnthe1@gmail.com> Date: Fri, 7 May 2021 06:13:57 +0800 Subject: [PATCH 17/17] Update types.md --- docs/basic/troubleshooting/types.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/docs/basic/troubleshooting/types.md b/docs/basic/troubleshooting/types.md index f46da4c9..41aed27b 100644 --- a/docs/basic/troubleshooting/types.md +++ b/docs/basic/troubleshooting/types.md @@ -543,3 +543,29 @@ declare module "react-router-dom" { ``` For more information on creating type definitions for class components, you can refer to this [post](https://templecoding.com/blog/2016/03/31/creating-typescript-typings-for-existing-react-components) for reference. + + +## Frequent Known Problems with TypeScript + +Just a list of stuff that React developers frequently run into, that TS has no solution for. Not necessarily TSX only. + +### TypeScript doesn't narrow after an object element null check + + +[](https://mobile.twitter.com/tannerlinsley/status/1390409931627499523) + +Ref: https://mobile.twitter.com/tannerlinsley/status/1390409931627499523. see also https://github.com/microsoft/TypeScript/issues/9998 + +### TypeScript doesn't let you restrict the type of children + +Guaranteeing typesafety for this kind of API isn't possible: + +```tsx +<Menu> + <MenuItem/> {/* ok */} + <MenuLink/> {/* ok */} + <div> {/* error */} +</Menu> +``` + +Source: https://twitter.com/ryanflorence/status/1085745787982700544?s=20