From 3f875c0d85f68d5c5a4c26ce0dcc862fec917edd Mon Sep 17 00:00:00 2001 From: Ferdy Budhidharma Date: Tue, 5 Feb 2019 10:33:33 -0600 Subject: [PATCH 001/791] updates for hooks --- README.md | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/README.md b/README.md index 80b7b8c9..02605389 100644 --- a/README.md +++ b/README.md @@ -212,6 +212,31 @@ const [user, setUser] = useState(null); setUser(newUser); ``` +**useRef** + +When using `useRef`, you have two options when creating a ref container that does not have an initial value: + +```ts +const ref1 = useRef(null) +const ref2 = useRef(null) +``` + +The first option will make `ref1.current` read-only, 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). + +The second option will make `ref2.current` mutable, and is intended for "instance variables" that you manage yourself. + +**useEffect** + +When using `useEffect`, take care not to return anything other than a function or `undefined`, otherwise both TypeScript and React will yell at you. This can be subtle when using arrow functions: + +```ts +function DelayedEffect(props: { timerMs: number }) { + // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces + useEffect(() => setTimeout(() => {/* do stuff */}, props.timerMs), [timerMs]) + return null +} +``` + **Custom Hooks** If you are returning an array in your Custom Hook, you will want to avoid type inference as Typescript will infer a union type (when you actually want different types in each position of the array). Instead, assert or define the function return types: @@ -230,6 +255,13 @@ export function useLoading() { } ``` +A helper function that automatically types tuples can also be helpful if you write a lot of custom hooks: +```ts +function tuplify(...elements: T) { return elements } + +const myTuple = tuplify(false, 1, 'a') // type is [boolean, number, string] +``` + If you are writing a React Hooks library, don't forget that you should also expose your types for users to use. Example React Hooks + TypeScript Libraries: From 3481b5b4442a00d34edbd35beefd8c4ebe3a7759 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 5 Feb 2019 11:30:53 -0600 Subject: [PATCH 002/791] Update README.md Co-Authored-By: ferdaber --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 02605389..59cdf6c4 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,8 @@ When using `useEffect`, take care not to return anything other than a function o ```ts function DelayedEffect(props: { timerMs: number }) { // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces - useEffect(() => setTimeout(() => {/* do stuff */}, props.timerMs), [timerMs]) + const { timerMs } = props; + useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) return null } ``` From 1e0ee48d095a4f74762d96ee5218e597357c2b72 Mon Sep 17 00:00:00 2001 From: Ferdy Budhidharma Date: Tue, 5 Feb 2019 11:37:49 -0600 Subject: [PATCH 003/791] Update README.md --- README.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 59cdf6c4..7acb1163 100644 --- a/README.md +++ b/README.md @@ -260,8 +260,19 @@ A helper function that automatically types tuples can also be helpful if you wri ```ts function tuplify(...elements: T) { return elements } -const myTuple = tuplify(false, 1, 'a') // type is [boolean, number, string] +function useArray() { + const numberValue = useRef(3).current + const functionValue = useRef(() => {}).current + return [numberValue, functionValue] // type is (number | (() => void))[] +} + +function useTuple() { + const numberValue = useRef(3).current + const functionValue = useRef(() => {}).current + return tuplify(numberValue, functionValue) // type is [number, () => void] +} ``` +The React team recommends that custom hooks that return more than two values should use proper objects instead of tuples, however. If you are writing a React Hooks library, don't forget that you should also expose your types for users to use. From d901e5cd5f768b5a1d6727bbac788635e7fa55fb Mon Sep 17 00:00:00 2001 From: Jake Wiesler Date: Thu, 7 Feb 2019 11:04:39 -0500 Subject: [PATCH 004/791] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7acb1163..328603cd 100644 --- a/README.md +++ b/README.md @@ -191,7 +191,7 @@ const MyArrayComponent = () => (Array(5).fill(
) as any) as JSX.Element; ## Hooks -Hooks are supported in `@types/react` from v16.7 up. +Hooks are supported in `@types/react` from v16.8 up. **useState** From 2cdeeb5d2b356f3058d6f75e7e6a2c05c96c72fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julio=20Pav=C3=B3n?= Date: Fri, 8 Feb 2019 23:04:15 +0100 Subject: [PATCH 005/791] update @jpavon/react-scripts-ts package description --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 328603cd..aa36d7fa 100644 --- a/README.md +++ b/README.md @@ -232,7 +232,7 @@ When using `useEffect`, take care not to return anything other than a function o ```ts function DelayedEffect(props: { timerMs: number }) { // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces - const { timerMs } = props; + const { timerMs } = props; useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) return null } @@ -408,7 +408,7 @@ let el = ;
Typescript 2.9 and earlier - + 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: ```ts @@ -907,7 +907,7 @@ partialStateUpdate({ foo: 2 }); // this works Minor caveats on using Partial - + Note that there are some TS users who don't agree with using `Partial` as it behaves today. See [subtle pitfalls of the above example here](https://twitter.com/ferdaber/status/1084798596027957248), and check out this long discussion on [why @types/react uses Pick instead of Partial](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365).
@@ -1044,7 +1044,7 @@ const f = (e: PlotlyHTMLElement) => { React Boilerplates: -- [jpavon](https://github.com/jpavon/react-scripts-ts) offers an alternative react-scripts-ts with Webpack 4 and better linting. +- [@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) - [webpack config tool](https://webpack.jakoblind.no/) is a visual tool for creating webpack projects with React and TypeScript - ready to go template with [Material-UI](https://material-ui.com/), routing and Redux From 36ee450326e6b2250d6a25d0ece8386428f83dd3 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 13 Feb 2019 01:56:02 -1000 Subject: [PATCH 006/791] recommend Marius Schulz --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 751501d7..0630a9fa 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -426,7 +426,7 @@ Sometimes DefinitelyTyped can get it wrong, or isn't quite addressing your use c TypeScript Versions often introduce new ways to do things; this section helps current users of React + TypeScript upgrade TypeScript versions and explore patterns commonly used by TypeScript + React apps and libraries. This may have duplications with other sections; if you spot any discrepancies, [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new)! -*TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!* +*TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!* Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). ## TypeScript 2.9 From c992d75348e9179c90f3833fea6c79f670c84341 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 13 Feb 2019 02:51:55 -1000 Subject: [PATCH 007/791] add utility types section --- ADVANCED.md | 112 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 29 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 0630a9fa..f1b662af 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -13,6 +13,7 @@ Expand Table of Contents +- [Section 0: Utility Types](#section-0-utility-types) - [Section 1: Reusable Components/Type Utilities](#section-1-reusable-componentstype-utilities) * [Higher Order Components](#higher-order-components-hocs) * [Render Props](#render-props) @@ -40,6 +41,88 @@ - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) +# Section 0: Utility Types + +Handy Utility Types used in the rest of this cheatsheet, or commonly used with React+TS apps, with explanation on what they do and how they can help. We will assume knowledge of [mapped types and conditional types](https://mariusschulz.com/blog/series/typescript-evolution) like `Exclude` and `ReturnType` but try to build progressively upon them. + +
+ + Omit<T, K extends keyof T>: Subtract keys from one interface from the other. + + +```ts +/** + * Subtract keys from one interface from the other. + * + * @example + * interface One { one: string } + * interface Three { one: string, two: string } + * + * type Two = Omit; + * + * // The type of Two will be + * interface Two { two: string } + */ +type Omit = Pick>; +``` + +You can also supply string literals to omit: + +```ts +type SettingsPageProps = Omit +``` + +
+ +
+ + Optionalize<T extends K, K>: Remove from T the keys that are in common with K + + +```ts +/** + * Remove from T the keys that are in common with K + */ +type Optionalize = Omit; +``` + + An example usage is in our HOC section below. + +
+
+ + Nullable<T> or Maybe<T>: Make a Type into a Maybe Type + + +```ts +/** + * Make a Type into a Maybe Type + */ +type Nullable = T | null +type Maybe = T | undefined +``` + + Your choice of `null` or `undefined` depends on your approach toward missing values. Some folks feel strongly one way or the other. + +
+
+ + Dictionary<T>: Dictionary of string, value pairs + + +```ts +/** + * Dictionary of string, value pairs + */ +type Dictionary = { [key: string]: T } +``` + + `[key: string]` is a very handy trick in general. You can also modify dictionary fields with [Readonly](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html) or make them optional or Omit them, etc. + +
+ +[Something to add? File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new). We respect the fact that naming and selection of examples here is arbitrary as the possible space is infinite. + # Section 1: Advanced Guides ## Higher Order Components (HoCs) @@ -88,29 +171,6 @@ Now when consuming the component you can omit the `primaryColor` prop or overrid **Declaring the HoC** -The following utilities will be needed. - -```ts -/** - * Generic type utility to subtract keys from one interface from the other. - * - * @example - * interface One { one: string } - * interface Three { one: string, two: string } - * - * type Two = Omit; - * - * // The type of Two will be - * interface Two { two: string } - */ -type Omit = Pick>; - -/** - * Remove from T the keys that are in common with K - */ -type Optionalize = Omit; -``` - The actual HoC. ```ts @@ -222,7 +282,6 @@ function Clickable(props: ButtonProps | AnchorProps) { They don't even need to be completely different props, as long as they have at least one difference in properties: ```tsx -type Omit = Pick> type LinkProps = Omit & { to?: string } function RouterLink(props: LinkProps): JSX.Element @@ -242,10 +301,7 @@ function RouterLink(props: LinkProps | AnchorProps) { Here is an example solution, see the further discussion for other solutions. *thanks to [@jpavon](https://github.com/sw-yx/react-typescript-cheatsheet/issues/12#issuecomment-394440577)* ```tsx -type Omit = Pick> - interface LinkProps {} - type AnchorProps = React.AnchorHTMLAttributes type RouterLinkProps = Omit @@ -274,8 +330,6 @@ const Link = ( If you want to conditionally render a component, sometimes is better to use [React's composition model](https://reactjs.org/docs/composition-vs-inheritance.html) to have simpler components and better to understand typings: ```tsx -type Omit = Pick> - type AnchorProps = React.AnchorHTMLAttributes type RouterLinkProps = Omit From dccb0ca988c838e07bc6ec360227f49ab908a45e Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 14 Feb 2019 00:05:04 -1000 Subject: [PATCH 008/791] update React.FC and defaultProps recommendations update React.FC and defaultProps recommendations with reference to https://github.com/sw-yx/react-typescript-cheatsheet/issues/87 --- README.md | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index aa36d7fa..6dd0bca4 100644 --- a/README.md +++ b/README.md @@ -139,6 +139,7 @@ The former pattern is shorter, so why would people use `React.FunctionComponent` - If you need to use `children` property inside the function body, in the former case it has to be added explicitly. `FunctionComponent` already includes the correctly typed `children` property which then doesn't have to become part of your type. - Typing your function explicitly will also give you typechecking and autocomplete on its static properties, like `displayName`, `propTypes`, and `defaultProps`. - _In future_, it will also set `readonly` on your props just like `React.Component` does. +- HOWEVER, there are currently known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/sw-yx/react-typescript-cheatsheet/issues/87) - scroll down to our `defaultProps` section for typing recommendations there. ```tsx const Title: React.FunctionComponent<{ title: string }> = ({ @@ -387,9 +388,25 @@ class App extends React.Component<{ ## Typing defaultProps -For Typescript 3.0+, type inference [should work](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html), although [some edge cases are still problematic](https://github.com/sw-yx/react-typescript-cheatsheet/issues/61). Just type your props like normal. +For Typescript 3.0+, type inference [should work](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html), although [some edge cases are still problematic](https://github.com/sw-yx/react-typescript-cheatsheet/issues/61). Just type your props like normal, except don't use `React.FC`. ```tsx +// //////////////// +// function components +// //////////////// +type Props = { age: number } & typeof defaultProps; +const defaultProps = { + who: 'Johny Five', +}; + +const Greet = (props: Props) => { + /*...*/ +}; +Greet.defaultProps = defaultProps + +// //////////////// +// class components +// //////////////// export type Props = { name: string; }; @@ -405,6 +422,18 @@ export class Greet extends React.Component { // Type-checks! No type assertions needed! let el = ; ``` +
+ Why does React.FC break defaultProps? + + You can check the discussions here: + + - https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 + - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 + - https://github.com/sw-yx/react-typescript-cheatsheet/issues/87 + + This is just the current state and may be fixed in future. + +
Typescript 2.9 and earlier From 50de52206d63961213c2b101a894d74e536bf350 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 19 Feb 2019 03:57:39 -1000 Subject: [PATCH 009/791] add troubleshooting for importing images --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 6dd0bca4..139d146c 100644 --- a/README.md +++ b/README.md @@ -966,6 +966,20 @@ function foo(bar: string) { // inside your app, if you need { baz: number } type FooReturn = ReturnType; // { baz: number } ``` +# Troubleshooting Handbook: Images and other non-TS/TSX files + +Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): + +```ts +// declaration.d.ts +// anywhere in your project, NOT the same name as any of your .ts/tsx files +declare module '*.png' + +// importing in a tsx file +import * as logo from "./logo.png"; +``` + +Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 and [StackOverflow](https://stackoverflow.com/a/49715468/4216035) # Troubleshooting Handbook: TSLint From 8ffe6e51341d2b99f85680860ab9a4daaa84ce0f Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 19 Feb 2019 16:41:35 -1000 Subject: [PATCH 010/791] fix minor typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 139d146c..a2a1c565 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ If you want to use the `function` keyword instead of an arrow function, you can ```tsx const App: React.FunctionComponent<{ message: string }> = function App({ message }) { - return ; + return
{message}
; } ``` From dfde4671857614ff97fe1a42590461fa11a8e16e Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 19 Feb 2019 16:57:36 -1000 Subject: [PATCH 011/791] add notes on ComponentProps usage --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index a2a1c565..a05169fb 100644 --- a/README.md +++ b/README.md @@ -192,7 +192,7 @@ const MyArrayComponent = () => (Array(5).fill(
) as any) as JSX.Element; ## Hooks -Hooks are supported in `@types/react` from v16.8 up. +Hooks are [supported in `@types/react` from v16.8 up](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/5565fe5e46e329a5ee02ddf739abe11bf16f278d/types/react/index.d.ts#L765-L973). **useState** @@ -232,8 +232,8 @@ When using `useEffect`, take care not to return anything other than a function o ```ts function DelayedEffect(props: { timerMs: number }) { - // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces const { timerMs } = props; + // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) return null } @@ -694,6 +694,8 @@ export const FancyButton = React.forwardRef((props, ref) => ( )); ``` +If you are grabbing the props of a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L735). + More info: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 [Something to add? File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new). @@ -944,17 +946,19 @@ Note that there are some TS users who don't agree with using `Partial` as it beh This can be annoying but here are ways to grab the types! -- Grabbing the Prop types of a component: Use `typeof`, and optionally `Omit` any overlapping types +- Grabbing the Prop types of a component: Use `React.ComponentProps` and `typeof`, and optionally `Omit` any overlapping types ```tsx -import { Button } from 'library'; // but doesn't export ButtonProps -type ButtonProps = React.ComponentProps; // grab your own +import { Button } from 'library'; // but doesn't export ButtonProps! oh no! +type ButtonProps = React.ComponentProps; // no problem! grab your own! type AlertButtonProps = Omit; // modify const AlertButton: React.FC = props => (
+ # Section 0: Utility Types @@ -69,7 +70,10 @@ type Omit = Pick>; You can also supply string literals to omit: ```ts -type SettingsPageProps = Omit +type SettingsPageProps = Omit< + ServerConfig, + 'immutableSetting1' | 'invisibleSetting2' +>; ``` @@ -102,8 +106,8 @@ type Nullable = T | null type Maybe = T | undefined ``` - Your choice of `null` or `undefined` depends on your approach toward missing values. Some folks feel strongly one way or the other. - +Your choice of `null` or `undefined` depends on your approach toward missing values. Some folks feel strongly one way or the other. +
@@ -117,10 +121,16 @@ type Maybe = T | undefined type Dictionary = { [key: string]: T } ``` - `[key: string]` is a very handy trick in general. You can also modify dictionary fields with [Readonly](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html) or make them optional or Omit them, etc. - +`[key: string]` is a very handy trick in general. You can also modify dictionary fields with [Readonly](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html) or make them optional or Omit them, etc. +
+There also exist helper type libraries: + +- [utility-types](https://github.com/piotrwitek/utility-types) +- [type-zoo](https://github.com/pelotom/type-zoo) +- [typesafe-actions](https://github.com/piotrwitek/typesafe-actions) + [Something to add? File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new). We respect the fact that naming and selection of examples here is arbitrary as the possible space is infinite. # Section 1: Advanced Guides @@ -142,7 +152,6 @@ interface WithThemeProps { 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. ```ts - interface Props extends WithThemeProps { children: ReactNode; } @@ -174,12 +183,17 @@ Now when consuming the component you can omit the `primaryColor` prop or overrid The actual HoC. ```ts -export function withTheme(WrappedComponent: React.ComponentType) { +export function withTheme( + WrappedComponent: React.ComponentType +) { // Try to create a nice displayName for React Dev Tools. - const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component"; + const displayName = + WrappedComponent.displayName || WrappedComponent.name || 'Component'; // Creating the inner component. The calculated Props type here is the where the magic happens. - return class ComponentWithTheme extends React.Component> { + return class ComponentWithTheme extends React.Component< + Optionalize + > { public static displayName = `withPages(${displayName})`; public render() { @@ -189,23 +203,24 @@ export function withTheme(WrappedComp // this.props comes afterwards so the can override the default ones. return ; } - } + }; } ``` Note that the `{...this.props as T}` assertion is needed because of a current bug in TS 3.2 https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046 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: + ```ts // inject static values to a component so that they're always provided export function inject( Component: React.JSXElementConstructor, injector: Pick - ) { - return function Injected(props: Omit) { - return - } - } +) { + return function Injected(props: Omit) { + return ; + }; +} ``` ### Using `forwardRef` @@ -247,9 +262,9 @@ export interface Props { ```tsx function PassThrough(props: { as: ReactType }) { - const { as: Component } = props; + const { as: Component } = props; - return + return ; } ``` @@ -260,37 +275,41 @@ function PassThrough(props: { as: ReactType }) { Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". A very common use case for this is to render something as either a button or an anchor, based on if it receives a `href` attribute. + ```tsx -type ButtonProps = JSX.IntrinsicElements['button'] -type AnchorProps = JSX.IntrinsicElements['a'] +type ButtonProps = JSX.IntrinsicElements['button']; +type AnchorProps = JSX.IntrinsicElements['a']; // optionally use a custom type guard -function isPropsForAnchorElement(props: ButtonProps | AnchorProps): props is AnchorProps { - return 'href' in props +function isPropsForAnchorElement( + props: ButtonProps | AnchorProps +): props is AnchorProps { + return 'href' in props; } -function Clickable(props: ButtonProps): JSX.Element -function Clickable(props: AnchorProps): JSX.Element +function Clickable(props: ButtonProps): JSX.Element; +function Clickable(props: AnchorProps): JSX.Element; function Clickable(props: ButtonProps | AnchorProps) { if (isPropsForAnchorElement(props)) { - return + return ; } else { - return
-) +); ``` - ## Props: Must Pass Both ```tsx -type OneOrAnother = +type OneOrAnother = | (T1 & { [K in keyof T2]?: undefined }) - | (T2 & { [K in keyof T1]?: undefined }) + | (T2 & { [K in keyof T1]?: undefined }); -type Props = OneOrAnother<{ a: string; b: string }, {}> +type Props = OneOrAnother<{ a: string; b: string }, {}>; -const a: Props = { a: 'a' } // error -const b: Props = { b: 'b' } // error -const ab: Props = { a: 'a', b: 'b' } // ok +const a: Props = { a: 'a' }; // error +const b: Props = { b: 'b' }; // error +const ab: Props = { a: 'a', b: 'b' }; // ok ``` Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) @@ -411,10 +429,10 @@ Sometimes when intersecting types, we want to define our own version of an attri ```tsx export interface Props { - label: React.ReactNode // this will conflict with the InputElement's label + label: React.ReactNode; // this will conflict with the InputElement's label } -type Omit = Pick> +type Omit = Pick>; // usage export const Checkbox = ( @@ -422,12 +440,9 @@ export const Checkbox = ( ) => { const { label } = props; return ( -
- + return ; } else { - return
-) +); ``` - ## Props: Must Pass Both ```tsx -type OneOrAnother = +type OneOrAnother = | (T1 & { [K in keyof T2]?: undefined }) - | (T2 & { [K in keyof T1]?: undefined }) + | (T2 & { [K in keyof T1]?: undefined }); -type Props = OneOrAnother<{ a: string; b: string }, {}> +type Props = OneOrAnother<{ a: string; b: string }, {}>; -const a: Props = { a: 'a' } // error -const b: Props = { b: 'b' } // error -const ab: Props = { a: 'a', b: 'b' } // ok +const a: Props = { a: 'a' }; // error +const b: Props = { b: 'b' }; // error +const ab: Props = { a: 'a', b: 'b' }; // ok ``` Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) @@ -411,10 +429,10 @@ Sometimes when intersecting types, we want to define our own version of an attri ```tsx export interface Props { - label: React.ReactNode // this will conflict with the InputElement's label + label: React.ReactNode; // this will conflict with the InputElement's label } -type Omit = Pick> +type Omit = Pick>; // usage export const Checkbox = ( @@ -422,12 +440,9 @@ export const Checkbox = ( ) => { const { label } = props; return ( -
-
+
;
); @@ -100,44 +100,55 @@ function BlogPost({ data, id }: BlogPostProps) { Example HOC from React Docs translated to TypeScript ```tsx -// // ACTUAL HOC -interface WithSubscriptionProps { - data: TODO_ANY; +// these are the props to be injected by the HOC +interface WithDataProps { + data: T; // data is generic } -/** This function takes a component... */ -function withSubscription< - T extends WithSubscriptionProps = WithSubscriptionProps ->( - WrappedComponent: React.ComponentType, - selectData: (DataSource: DataType, props: TODO_ANY) => TODO_ANY +// T is the type of data +// P is the props of the wrapped component that is inferred +// C is the actual interface of the wrapped component (used to grab defaultProps from it) +export function withSubscription, C>( + // this type allows us to infer P, but grab the type of WrappedComponent separately without it interfering with the inference of P + WrappedComponent: JSXElementConstructor

& C, + // selectData is a functor for T + // props is Readonly because it's readonly inside of the class + selectData: ( + dataSource: typeof DataSource, + props: Readonly>> + ) => T ) { - // ...and returns another component... - return class extends React.Component> { - state = { - data: selectData(DataSource, this.props) - }; - - componentDidMount() { - // ... that takes care of the subscription... - DataSource.addChangeListener(this.handleChange); + // the magic is here: JSX.LibraryManagedAttributes will take the type of WrapedComponent and resolve its default props + // against the props of WithData, which is just the original P type with 'data' removed from its requirements + type Props = JSX.LibraryManagedAttributes>; + type State = { + data: T; + }; + return class WithData extends Component { + constructor(props: Props) { + super(props); + this.handleChange = this.handleChange.bind(this); + this.state = { + data: selectData(DataSource, props) + }; } - componentWillUnmount() { + componentDidMount = () => DataSource.addChangeListener(this.handleChange); + + componentWillUnmount = () => DataSource.removeChangeListener(this.handleChange); - } - handleChange = () => { + handleChange = () => this.setState({ data: selectData(DataSource, this.props) }); - }; render() { - // ... and renders the wrapped component with the fresh data! - // Notice that we pass through any additional props - return ; + // the typing for spreading this.props is... very complex. best way right now is to just type it as any + // data will still be typechecked + return ; } }; + // return WithData; } /** HOC usage with Components */ @@ -148,7 +159,7 @@ export const CommentListWithSubscription = withSubscription( export const BlogPostWithSubscription = withSubscription( BlogPost, - (DataSource: DataType, props: React.ComponentProps) => + (DataSource: DataType, props: Omit) => DataSource.getBlogPost(props.id) ); ``` From d51511ca613df6c211d2b8a5e466ea9311e692cc Mon Sep 17 00:00:00 2001 From: sw-yx Date: Wed, 20 Feb 2019 09:04:22 -1000 Subject: [PATCH 017/791] fix HOC example --- HOC.md | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/HOC.md b/HOC.md index 8684c457..e2ba354a 100644 --- a/HOC.md +++ b/HOC.md @@ -206,11 +206,6 @@ Here we build our own mini `connect` to understand HOCs: ```tsx /** utility types we use */ type Omit = Pick>; -type Optionalize = Omit; -// // ACTUAL HOC -interface WithSubscriptionProps { - data: TODO_ANY; -} /** dummy Data */ type CommentType = { text: string; id: number }; @@ -226,9 +221,8 @@ const comments: CommentType[] = [ ]; /** dummy child components that take anything */ const Comment = (_: any) => null; -type TODO_ANY = any; /** Rewritten Components from the React docs that just uses injected data prop */ -function CommentList({ data }: WithSubscriptionProps) { +function CommentList({ data }: WithSubscriptionProps) { return (

{data.map((comment: CommentType) => ( @@ -254,14 +248,17 @@ const ConnectedComment = connect( commentActions )(CommentList); +// these are the props to be injected by the HOC +interface WithSubscriptionProps { + data: T; +} function connect(mapStateToProps: Function, mapDispatchToProps: Function) { - return function( + return function, C>( WrappedComponent: React.ComponentType ) { + type Props = JSX.LibraryManagedAttributes>; // Creating the inner component. The calculated Props type here is the where the magic happens. - return class ComponentWithTheme extends React.Component< - Optionalize - > { + return class ComponentWithTheme extends React.Component { public render() { // Fetch the props you want inject. This could be done with context instead. const mappedStateProps = mapStateToProps(this.state, this.props); @@ -269,7 +266,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { // this.props comes afterwards so the can override the default ones. return ( From 3114f3aea858a480a18a888e7aee0c7fe421a232 Mon Sep 17 00:00:00 2001 From: sw-yx Date: Wed, 20 Feb 2019 09:58:15 -1000 Subject: [PATCH 018/791] add links --- HOC.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/HOC.md b/HOC.md index e2ba354a..86e4b6ae 100644 --- a/HOC.md +++ b/HOC.md @@ -22,7 +22,7 @@ In this first section we refer closely to [the React docs on HOCs](https://reactjs.org/docs/higher-order-components.html) and offer direct TypeScript parallels. -## Docs Example: Use HOCs For Cross-Cutting Concerns +## Docs Example: [Use HOCs For Cross-Cutting Concerns](https://reactjs.org/docs/higher-order-components.html#use-hocs-for-cross-cutting-concerns)
@@ -164,7 +164,7 @@ export const BlogPostWithSubscription = withSubscription( ); ``` -## Docs Example: Don’t Mutate the Original Component. Use Composition. +## Docs Example: [Don’t Mutate the Original Component. Use Composition.](https://reactjs.org/docs/higher-order-components.html#dont-mutate-the-original-component-use-composition) This is pretty straightforward - make sure to assert the passed props as `T` [due to the TS 3.2 bug](https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046). @@ -185,11 +185,11 @@ function logProps(WrappedComponent: React.ComponentType) { } ``` -## Docs Example: Pass Unrelated Props Through to the Wrapped Component +## Docs Example: [Pass Unrelated Props Through to the Wrapped Component](https://reactjs.org/docs/higher-order-components.html#convention-pass-unrelated-props-through-to-the-wrapped-component) No TypeScript specific advice needed here. -## Docs Example: Maximizing Composability +## Docs Example: [Maximizing Composability](https://reactjs.org/docs/higher-order-components.html#convention-maximizing-composability) HOCs can take the form of Functions that return Higher Order Components that return Components. @@ -277,7 +277,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { } ``` -## Docs Example: Wrap the Display Name for Easy Debugging +## Docs Example: [Wrap the Display Name for Easy Debugging](https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging) This is pretty straightforward as well. @@ -303,7 +303,7 @@ function getDisplayName(WrappedComponent: React.ComponentType) { } ``` -## Unwritten +## Unwritten: [Caveats section](https://reactjs.org/docs/higher-order-components.html#caveats) - Don’t Use HOCs Inside the render Method - Static Methods Must Be Copied Over From 9002d655630ca2cb2f60e72fbd5b4a514674a5f2 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 20 Feb 2019 10:12:09 -1000 Subject: [PATCH 019/791] add HOC cheatsheeta --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index a05169fb..dab33781 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,9 @@ Translations: - The goal is to take _full advantage_ of TypeScript. - **The Migrating Cheatsheet** ([`/MIGRATING.md`](/MIGRATING.md)) helps collate advice for incrementally migrating large codebases from JS or Flow, **from people who have done it**. - We do not try to convince people to switch, only to help people who have already decided + - ⚠️This is a new cheatsheet, all assistance is welcome +- **The HOC Cheatsheet** ([`/HOC.md`](/HOC.md)) specifically teaches people to write HOCs with examples. + - Familiarity with [Generics](https://www.typescriptlang.org/docs/handbook/generics.html) is necessary. - ⚠️This is the newest cheatsheet, all assistance is welcome --- From da333df7f7d7dc082120f55f78c3bb0f0a28e8bb Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 20 Feb 2019 10:43:00 -1000 Subject: [PATCH 020/791] add issue templates --- .github/ISSUE_TEMPLATE/advanced-cheatsheet.md | 16 ++++++++++++++++ .github/ISSUE_TEMPLATE/basic-cheatsheet.md | 16 ++++++++++++++++ .../ISSUE_TEMPLATE/general-react-ts-question.md | 10 ++++++++++ .github/ISSUE_TEMPLATE/hoc-cheatsheet.md | 16 ++++++++++++++++ .github/ISSUE_TEMPLATE/migrating-cheatsheet.md | 16 ++++++++++++++++ 5 files changed, 74 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/advanced-cheatsheet.md create mode 100644 .github/ISSUE_TEMPLATE/basic-cheatsheet.md create mode 100644 .github/ISSUE_TEMPLATE/general-react-ts-question.md create mode 100644 .github/ISSUE_TEMPLATE/hoc-cheatsheet.md create mode 100644 .github/ISSUE_TEMPLATE/migrating-cheatsheet.md diff --git a/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md new file mode 100644 index 00000000..285145f3 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md @@ -0,0 +1,16 @@ +--- +name: Advanced Cheatsheet +about: Report Issue/Suggest an idea for Advanced Cheatsheet +title: "[Advanced] ISSUE_TITLE_HERE" +labels: ADVANCED +assignees: '' + +--- + +**What cheatsheet is this about? (if applicable)** + +Advanced cheatsheet + +**What's your issue or idea?** + +*Write here* diff --git a/.github/ISSUE_TEMPLATE/basic-cheatsheet.md b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md new file mode 100644 index 00000000..12a2df76 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md @@ -0,0 +1,16 @@ +--- +name: Basic Cheatsheet +about: Report Issue/Suggest an idea for Basic Cheatsheet +title: "[Basic] ISSUE_TITLE_HERE" +labels: BASIC +assignees: sw-yx + +--- + +**What cheatsheet is this about? (if applicable)** + +Basic cheatsheet + +**What's your issue or idea?** + +*Write here* diff --git a/.github/ISSUE_TEMPLATE/general-react-ts-question.md b/.github/ISSUE_TEMPLATE/general-react-ts-question.md new file mode 100644 index 00000000..e73563b0 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/general-react-ts-question.md @@ -0,0 +1,10 @@ +--- +name: General React+TS Question +about: Questions are welcome! We want to know and solve your pain points. +title: "[Question] QUESTION_TITLE_HERE" +labels: good first issue +assignees: sw-yx + +--- + + diff --git a/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md new file mode 100644 index 00000000..9b6407fe --- /dev/null +++ b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md @@ -0,0 +1,16 @@ +--- +name: HOC Cheatsheet +about: Report Issue/Suggest an idea for HOC Cheatsheet +title: "[HOC] ISSUE_TITLE_HERE" +labels: HOC +assignees: '' + +--- + +**What cheatsheet is this about? (if applicable)** + +HOC cheatsheet + +**What's your issue or idea?** + +*Write here* diff --git a/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md new file mode 100644 index 00000000..458f5c77 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md @@ -0,0 +1,16 @@ +--- +name: Migrating Cheatsheet +about: Report Issue/Suggest an idea for Migrating Cheatsheet +title: "[Migrating] ISSUE_TITLE_HERE" +labels: MIGRATING +assignees: '' + +--- + +**What cheatsheet is this about? (if applicable)** + +Migrating cheatsheet + +**What's your issue or idea?** + +*Write here* From c9a79f6ec0e6ff39a81b2dbe6ed0870ddffa9e4c Mon Sep 17 00:00:00 2001 From: latviancoder Date: Fri, 22 Feb 2019 21:24:42 +0100 Subject: [PATCH 021/791] add useReducer example --- README.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/README.md b/README.md index dab33781..466128da 100644 --- a/README.md +++ b/README.md @@ -242,6 +242,33 @@ function DelayedEffect(props: { timerMs: number }) { } ``` +**useReducer** + +You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. + +```tsx +type Action = + { type: 'SET_ONE'; payload: string; } + | { type: 'SET_TWO'; payload: number; }; + +export function reducer(state: AppState, action: Action): AppState { + switch (action.type) { + case 'SET_ONE': + return { + ...state, + one: action.payload // `payload` is string + }; + case 'SET_TWO': + return { + ...state, + two: action.payload // `payload` is number + }; + default: + return state; + } +} +``` + **Custom Hooks** If you are returning an array in your Custom Hook, you will want to avoid type inference as Typescript will infer a union type (when you actually want different types in each position of the array). Instead, assert or define the function return types: From 5dc83f1ae9b11f5b2fe79c50931d7451ee000c38 Mon Sep 17 00:00:00 2001 From: jpangelle Date: Tue, 5 Mar 2019 13:21:45 -0600 Subject: [PATCH 022/791] Fix grammar/semantic issues, clean up whitespace --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index dab33781..90f49860 100644 --- a/README.md +++ b/README.md @@ -82,7 +82,7 @@ This guide will always assume you are starting with the latest TypeScript versio ## React + TypeScript Starter Kits -1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npm create react-app my-new-react-typescript-app --typescript` +1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app my-new-react-typescript-app --typescript` - We used to recommend `create-react-app-typescript` but it is now [deprecated](https://www.reddit.com/r/reactjs/comments/a5919a/createreactapptypescript_has_been_archived_rip/). [see migration instructions](https://vincenttunru.com/migrate-create-react-app-typescript-to-create-react-app/) @@ -427,15 +427,15 @@ let el = ; ```
Why does React.FC break defaultProps? - + You can check the discussions here: - + - https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 - https://github.com/sw-yx/react-typescript-cheatsheet/issues/87 - + This is just the current state and may be fixed in future. - +
@@ -750,7 +750,7 @@ _Not written yet._ watch for more o # Basic Troubleshooting Handbook: Types -Facing weird type errors? You aren't alone. This is the hardest part of using TypeScript with React. Be patience - you are learning a new language after all. However, the more you get good at this, the less time you'll be working _against_ the compiler and the more the compiler will be working _for_ you! +Facing weird type errors? You aren't alone. This is the hardest part of using TypeScript with React. Be patient - you are learning a new language after all. However, the more you get good at this, the less time you'll be working _against_ the compiler and the more the compiler will be working _for_ you! Try to avoid typing with `any` as much as possible to experience the full benefits of typescript. Instead, let's try to be familiar with some of the common strategies to solve these issues. @@ -804,7 +804,7 @@ function isAdmin(usr: Admin | User): usr is Admin { } ``` -If you need `if/elseif` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) if you need help. (See also: [Basarat's writeup](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html)). This is handy in typing reducers for `useReducer` or Redux. +If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) if you need help. (See also: [Basarat's writeup](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html)). This is handy in typing reducers for `useReducer` or Redux. ## Optional Types @@ -978,7 +978,7 @@ type FooReturn = ReturnType; // { baz: number } Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): ```ts -// declaration.d.ts +// declaration.d.ts // anywhere in your project, NOT the same name as any of your .ts/tsx files declare module '*.png' @@ -990,7 +990,7 @@ Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 a # Troubleshooting Handbook: TSLint -Sometimes TSLint is just getting in the way. Judicious turning off of things can be helpful. Here are useful tslint disables you may use: +Sometimes TSLint is just getting in the way. Judiciously turning off of things can be helpful. Here are useful tslint disables you may use: - `/* tslint:disable */` total disable - `// tslint:disable-line` just this line From a10dd2e361cdac103cc0b091bf757eaf2ebb9819 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:13:04 -0700 Subject: [PATCH 023/791] add logo v1 --- README.md | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 90f49860..0cd6faaa 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,34 @@ -:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@IslamAttrash](https://twitter.com/IslamAttrash) and [@ferdaber](https://twitter.com/ferdaber), we're so happy you want to try out TypeScript with React! This is meant to be a guide for React developers familiar with the concepts of TypeScript but who are just getting started writing their first React + TypeScript apps. If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new)! :+1: +
+

React+TypeScript Cheatsheets

+ +
+ react + ts logo + + +

Cheatsheets for experienced React developers getting started with TypeScript

+ +[**Basic**](https://github.com/sw-yx/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | +[**Advanced**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/ADVANCED.md) | +[**Migrating**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/MIGRATING.md) | +[**HOC**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/HOC.md) | +[中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[Contribute!](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | +[Ask!](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose) + +
-Translations: -- [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) _maintained by [@fi3ework](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn)_ -- Your language here? +:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose)! :+1: --- -## React + TypeScript Cheatsheets +## All React + TypeScript Cheatsheets - **The Basic Cheatsheet** ([`/README.md`](/README.md)) is focused on helping React devs just start using TS in React **apps** - focus on opinionated best practices, copy+pastable examples From 00d196a70271fcf1d3cee0cc761ce5321a8ca295 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:17:28 -0700 Subject: [PATCH 024/791] Update ADVANCED.md --- ADVANCED.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 44b94b25..4cd28dc2 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1,3 +1,29 @@ +
+ + + react + ts logo + + +

Cheatsheets for experienced React developers getting started with TypeScript

+ +[**Basic**](https://github.com/sw-yx/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | +[**Advanced**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/ADVANCED.md) | +[**Migrating**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/MIGRATING.md) | +[**HOC**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/HOC.md) | +[中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[Contribute!](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | +[Ask!](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose) + +
+ +--- + # Advanced Cheatsheet **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**. From d630bdb3af6ef10a0f0eabbf9eaa6926ec4d7361 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:17:39 -0700 Subject: [PATCH 025/791] Update ADVANCED.md From 0bf6dfe5fdb0272e028955999337ccf105ffa005 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:18:12 -0700 Subject: [PATCH 026/791] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0cd6faaa..da36ba9a 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@
+--- :wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose)! :+1: From 08d66b6e6fd4ea111a854ce1d9a7dfbaf1345c30 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:18:51 -0700 Subject: [PATCH 027/791] Update MIGRATING.md --- MIGRATING.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/MIGRATING.md b/MIGRATING.md index 097c414b..740e669c 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -1,3 +1,29 @@ +
+ + + react + ts logo + + +

Cheatsheets for experienced React developers getting started with TypeScript

+ +[**Basic**](https://github.com/sw-yx/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | +[**Advanced**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/ADVANCED.md) | +[**Migrating**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/MIGRATING.md) | +[**HOC**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/HOC.md) | +[中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[Contribute!](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | +[Ask!](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose) + +
+ +--- + # Migrating (to TypeScript) Cheatsheet 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. From 1ca22c373c3362a957aacf23c10826eec3c85a60 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:19:01 -0700 Subject: [PATCH 028/791] Update HOC.md --- HOC.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/HOC.md b/HOC.md index 86e4b6ae..b37524a4 100644 --- a/HOC.md +++ b/HOC.md @@ -1,3 +1,29 @@ +
+ + + react + ts logo + + +

Cheatsheets for experienced React developers getting started with TypeScript

+ +[**Basic**](https://github.com/sw-yx/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | +[**Advanced**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/ADVANCED.md) | +[**Migrating**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/MIGRATING.md) | +[**HOC**](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/HOC.md) | +[中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[Contribute!](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | +[Ask!](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose) + +
+ +--- + # HOC Cheatsheet **This HOC Cheatsheet** compiles all available knowledge for writing Higher Order Components with React and TypeScript. From 0f82bdf56238046b9510cbd20e3d0bf4f51a02dd Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:20:06 -0700 Subject: [PATCH 029/791] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index da36ba9a..3d2dfd93 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ react + ts logo Date: Wed, 6 Mar 2019 02:21:56 -0700 Subject: [PATCH 030/791] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3d2dfd93..6075fd87 100644 --- a/README.md +++ b/README.md @@ -25,8 +25,12 @@ --- +
+ :wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose)! :+1: +
+ --- ## All React + TypeScript Cheatsheets From 0d70b5cf94a63a4de551da7badf9d3c5ece1df46 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:22:25 -0700 Subject: [PATCH 031/791] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 6075fd87..21af2d4b 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,6 @@
---- -
:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose)! :+1: From 551a07433531d389cf5594138dbfe4392375e97b Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 6 Mar 2019 02:22:48 -0700 Subject: [PATCH 032/791] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 21af2d4b..6075fd87 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,8 @@
+--- +
:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new/choose)! :+1: From 5436ed399d9a96e385324a63fa355129da3d2d45 Mon Sep 17 00:00:00 2001 From: swyx Date: Sun, 17 Mar 2019 16:19:30 -0700 Subject: [PATCH 033/791] add some basic .d.ts instructions --- ADVANCED.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 4cd28dc2..7224bc01 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -880,15 +880,29 @@ An example github repository with a project showing how to integrate [eslint + t ## Working with Non-TypeScript Libraries (writing your own index.d.ts) -_Not written yet._ +Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: -Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/sw-yx/react-typescript-cheatsheet/issues/8). +``` +[ts] +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. + Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016] +``` + +So create a `.d.ts` file anywhere in your project with the module definition: + +```ts +// de-indent.d.ts +declare module 'de-indent' { + function deindent(): void + export = deindent // default export +} +```
Further Discussion -We have more discussion and examples [in our issue here](https://github.com/sw-yx/react-typescript-cheatsheet/issues/12). +Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/sw-yx/react-typescript-cheatsheet/issues/8). We have more discussion and examples [in our issue here](https://github.com/sw-yx/react-typescript-cheatsheet/issues/12).
From c01f1240acf35107421be57b3c3db4611057fed1 Mon Sep 17 00:00:00 2001 From: akameco Date: Wed, 20 Mar 2019 02:56:25 +0900 Subject: [PATCH 034/791] docs(ADVANCED.md): ReactType -> ElementType --- ADVANCED.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 7224bc01..44d2c099 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -284,10 +284,10 @@ export interface Props { ## `as` props (passing a component to be rendered) -`ReactType` is pretty useful to cover most types that can be passed to createElement e.g. +`ElementType` is pretty useful to cover most types that can be passed to createElement e.g. ```tsx -function PassThrough(props: { as: ReactType }) { +function PassThrough(props: { as: ElementType }) { const { as: Component } = props; return ; @@ -930,7 +930,7 @@ Most Commonly Used Interfaces and Types Not Commonly Used but Good to know - `Ref` - used to type `innerRef` -- `ReactType` - used for higher order components or operations on components +- `ElementType` - used for higher order components or operations on components - `ComponentType` - used for higher order components where you don't specifically deal with the intrinsic components - `ReactPortal` - used if you specifically need to type a prop as a portal, otherwise it is part of `ReactNode` - `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 @@ -957,6 +957,7 @@ Anything not listed above is considered an internal type and not public. If you' - `ComponentState` - `LegacyRef` - `StatelessComponent` +- `ReactType` ## `@types/react-dom` From 6a467f608bb2b540fce66f26cc39823d5b328835 Mon Sep 17 00:00:00 2001 From: Ivan Tanev Date: Sat, 6 Apr 2019 17:06:18 +0300 Subject: [PATCH 035/791] Improve the class component defaultProps example In the current example, we have a type definition for props but do not make use of it for defaultProps. This can lead to a mismatch between defaultProps and props. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6075fd87..5080b1cf 100644 --- a/README.md +++ b/README.md @@ -445,7 +445,7 @@ export class Greet extends React.Component { const { name } = this.props; return
Hello ${name.toUpperCase()}!
; } - static defaultProps = { name: 'world' }; + static defaultProps: Partial = { name: 'world' }; } // Type-checks! No type assertions needed! From 18009b24096127a763f664e9a4d4f705cb862164 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 8 Apr 2019 00:18:58 -0400 Subject: [PATCH 036/791] add warning notice for tslint --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 44d2c099..495cf03f 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -751,6 +751,8 @@ If you have specific advice in this area, please file a PR! _Contributed by: [@azdanov](https://github.com/sw-yx/react-typescript-cheatsheet/pull/14)_ +> ⚠️Note that [TSLint is now in maintenance and you should try to use ESLint instead](https://medium.com/palantir/tslint-in-2019-1a144c2317a9). The rest of this section is potentially outdated. + To use prettier with TSLint you will need [`tslint-config-prettier`](https://github.com/alexjoverm/tslint-config-prettier) which disables all the conflicting rules and optionally [`tslint-plugin-prettier`](https://github.com/ikatyang/tslint-plugin-prettier) which will highlight differences as TSLint issues. Example configuration: From 6cbdb4dda31155d4cd810dae37364d14a5454f89 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 9 Apr 2019 01:29:36 -0400 Subject: [PATCH 037/791] use "reverse" defaultProp type pattern as suggested in https://github.com/sw-yx/react-typescript-cheatsheet/pull/103 --- README.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5080b1cf..d63f0ec5 100644 --- a/README.md +++ b/README.md @@ -432,24 +432,24 @@ const Greet = (props: Props) => { /*...*/ }; Greet.defaultProps = defaultProps +``` -// //////////////// -// class components -// //////////////// -export type Props = { - name: string; -}; +For **Class components**, there are [a couple ways to do it](https://github.com/sw-yx/react-typescript-cheatsheet/pull/103#issuecomment-481061483)(including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: -export class Greet extends React.Component { - render() { - const { name } = this.props; - return
Hello ${name.toUpperCase()}!
; +```tsx +type GreetProps = typeof Greet.defaultProps & { + age: number +} + +class Greet extends React.Component { + static defaultProps = { + name: 'world' } - static defaultProps: Partial = { name: 'world' }; + /*...*/ } // Type-checks! No type assertions needed! -let el = ; +let el = ; ```
Why does React.FC break defaultProps? From 007d5e74ee7073ce790705e2d11056e59ea1a29b Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 13 Apr 2019 16:12:28 +0530 Subject: [PATCH 038/791] sindresorhus style guide --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d63f0ec5..3568c7a4 100644 --- a/README.md +++ b/README.md @@ -1151,6 +1151,7 @@ React Native Boilerplates: _contributed by [@spoeck](https://github.com/sw-yx/re - [Ultimate React Component Patterns with TypeScript 2.8](https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935) - [Basarat's TypeScript gitbook has a React section](https://basarat.gitbooks.io/typescript/content/docs/jsx/react.html) with an [Egghead.io course](https://egghead.io/courses/use-typescript-to-develop-react-applications) as well. - [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) +- [Sindre Sorhus' TypeScript Style Guide](https://github.com/sindresorhus/typescript-definition-style-guide) - [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 doesnt seem to be frequently updated anymore. - [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 - Typescript conversion: From 0794f1d9a8576a31493347067ff71336a2ea932f Mon Sep 17 00:00:00 2001 From: Retsam Date: Mon, 15 Apr 2019 11:45:12 -0400 Subject: [PATCH 039/791] =?UTF-8?q?BASIC=20-=20Rework=20Function=20Compone?= =?UTF-8?q?nts=20section=20to=20prefer=20normal=20function=20=E2=80=A6=20(?= =?UTF-8?q?#104)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 3568c7a4..1531e633 100644 --- a/README.md +++ b/README.md @@ -145,30 +145,30 @@ Please PR or [File an issue](https://github.com/sw-yx/react-typescript-cheatshee ## Function Components -You can specify the type of props as you use them and rely on type inference: +These can be written as normal functions that take a `props` argument and return a JSX element. ```tsx -const App = ({ message }: { message: string }) =>
{message}
; +type AppProps { message: string }; /* could also use interface */ +const App = ({ message }: AppProps) =>
{message}
; ``` -Or you can use the provided generic type for function components: +
+ +What about `React.FC`/`React.FunctionComponent`? + +You can also write components with `React.FunctionComponent` (or the shorthand `React.FC`): ```tsx const App: React.FC<{ message: string }> = ({ message }) => (
{message}
-); // React.FunctionComponent also works +); ``` -
- -What's the difference? +Some differences from the "normal function" version: -The former pattern is shorter, so why would people use `React.FunctionComponent` at all? +- It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps` - **However**, there are currently known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/sw-yx/react-typescript-cheatsheet/issues/87) - scroll down to our `defaultProps` section for typing recommendations there. -- If you need to use `children` property inside the function body, in the former case it has to be added explicitly. `FunctionComponent` already includes the correctly typed `children` property which then doesn't have to become part of your type. -- Typing your function explicitly will also give you typechecking and autocomplete on its static properties, like `displayName`, `propTypes`, and `defaultProps`. -- _In future_, it will also set `readonly` on your props just like `React.Component` does. -- HOWEVER, there are currently known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/sw-yx/react-typescript-cheatsheet/issues/87) - scroll down to our `defaultProps` section for typing recommendations there. +- It provides an implicit definition of `children` (see below) - however there are some issues with the implicit `children` type (e.g. [DefinitelyTyped#33006](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006)), and it might considered better style to be explicit about components that consume `children`, anyway. ```tsx const Title: React.FunctionComponent<{ title: string }> = ({ @@ -177,13 +177,11 @@ const Title: React.FunctionComponent<{ title: string }> = ({ }) =>
{children}
; ``` -If you want to use the `function` keyword instead of an arrow function, you can use this syntax (using a function expression, instead of declaration): +- _In the future_, it may automatically mark props as `readonly`, though that's a moot point if the props object is destructured in the constructor. -```tsx -const App: React.FunctionComponent<{ message: string }> = function App({ message }) { - return
{message}
; -} -``` +- `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). + +In most cases it makes very little difference which syntax is used, but the `React.FC` syntax is slightly more verbose without providing clear advantage, so precedence was given to the "normal function" syntax.
From c8170af548854f751f874a0f03cf3e1882ee83c7 Mon Sep 17 00:00:00 2001 From: Andrew Byrd Date: Wed, 17 Apr 2019 10:14:08 -0400 Subject: [PATCH 040/791] Update README.md minor type alias syntax error (#107) Minor correction to type alias declaration. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 291e3a1b..20dfcc85 100644 --- a/README.md +++ b/README.md @@ -148,7 +148,7 @@ Please PR or [File an issue](https://github.com/sw-yx/react-typescript-cheatshee 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 */ +type AppProps = { message: string }; /* could also use interface */ const App = ({ message }: AppProps) =>
{message}
; ``` From 5e4b35f4d8cf0d4def111d6a2866a2b6d8d19422 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 22 Apr 2019 00:01:47 -0400 Subject: [PATCH 041/791] add useref notes --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index 20dfcc85..353beda4 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,32 @@ function DelayedEffect(props: { timerMs: number }) { } ``` +**useRef** + +```tsx +function TextInputWithFocusButton() { + // initialise with null, but tell TypeScript we are looking for an HTMLInputElement + const inputEl = useRef(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! */ } + + + + ); +} +``` + +example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) + **useReducer** You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. @@ -329,6 +355,11 @@ function useTuple() { ``` The React team recommends that custom hooks that return more than two values should use proper objects instead of tuples, however. +More Hooks + TypeScript reading: + +- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d +- https://fettblog.eu/typescript-react/hooks/#useref + If you are writing a React Hooks library, don't forget that you should also expose your types for users to use. Example React Hooks + TypeScript Libraries: From 9115ed7c3630e6e6674f41ce4acbbb926d077f6b Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 24 Apr 2019 00:15:22 -0400 Subject: [PATCH 042/791] add note on Uncontrolled components in forms --- README.md | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 353beda4..689dbe64 100644 --- a/README.md +++ b/README.md @@ -652,8 +652,7 @@ If performance is not an issue, inlining handlers is easiest as you can just use ```tsx const el = ( -
# Section 1: React HOC docs in TypeScript @@ -335,6 +337,64 @@ function getDisplayName(WrappedComponent: React.ComponentType) { - Static Methods Must Be Copied Over - Refs Aren’t Passed Through +# Section 2: Excluding Props + +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. + +Say we have a component: + +```tsx +type DogProps { + name: string + owner: string +} +function Dog({name, owner}: DogProps) { + return
Woof: {name}, Owner: {owner}
+} +``` + +And we have a `withOwner` HOC that injects the `owner`: + +```tsx +const OwnedDog = withOwner('swyx')(Dog) +``` + +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: + +```tsx +typeof OwnedDog // we want this to be equal to { name: string } + + // this should be fine + // this should have a typeError + // this should be fine + +// and the HOC should be reusable for completely different prop types! + +type CatProps { + lives: number + owner: string +} +function Cat({lives, owner}: CatProps) { + return
Meow: {lives}, Owner: {owner}
+} + +const OwnedCat = withOwner('swyx')(Cat) + + // this should be fine + // this should have a typeError + // this should be fine +``` + +So how do we type `withOwner`? + +1. We get the types of the component: `keyof T` +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` +3. (optional) Use intersection types if you have more to exclude: `Exclude` +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 }` +5. (optional) Instead of writing this manually each time, we could use this utility: `type Omit = Pick>` +6. Now we write the HOC as a generic function. + + ## Good articles We will need to extract lessons from here in future but here they are: From 17fd8283c3221db9fe00b155dcf5a6abb1de3fec Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 4 May 2019 04:15:35 -0400 Subject: [PATCH 085/791] Update HOC.md --- HOC.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/HOC.md b/HOC.md index 44acb103..6ea5a740 100644 --- a/HOC.md +++ b/HOC.md @@ -392,7 +392,19 @@ So how do we type `withOwner`? 3. (optional) Use intersection types if you have more to exclude: `Exclude` 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 }` 5. (optional) Instead of writing this manually each time, we could use this utility: `type Omit = Pick>` -6. Now we write the HOC as a generic function. +6. Now we write the HOC as a generic function: + +```tsx +function withOwner(owner: string) { + return function (Component: React.ComponentType) { + return function (props: Omit): React.ReactNode { + return + } + } +} +``` + +*Note: above is an incomplete, nonworking example. PR a fix!* ## Good articles From 596e74756d08ea6731e44ab2ba29ed5a56e0d8f2 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 10 May 2019 00:18:45 -0400 Subject: [PATCH 086/791] add prettier and fix TOC --- ADVANCED.md | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 70c4f42d..c7a4d982 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -69,8 +69,8 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Commenting Components](#commenting-components) - [Design System Development](#design-system-development) - [Migrating from Flow](#migrating-from-flow) - - [Prettier + TSLint](#prettier--tslint) - - [ESLint + TSLint](#eslint--tslint) + - [Prettier](#prettier) + - [Linting](#linting) - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis)
@@ -759,6 +759,40 @@ If you have specific advice in this area, please file a PR! [Something to add? File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new). +## Prettier + +There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! + +```js +yarn add -D prettier husky lint-staged + +// inside package.json +{ + husky: { + hooks: { + 'pre-commit': 'lint-staged', + }, + }, + 'lint-staged': { + linters: { + '*.{ts,tsx,js,jsx,css,scss,md}': [ + 'prettier --trailing-comma es5 --single-quote --write', + 'git add', + ], + ignore: ['**/dist/*, **/node_modules/*'], + }, + }, + prettier: { + printWidth: 80, + semi: true, + singleQuote: true, + trailingComma: 'es5', + }, +} +``` + +This is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). + ## 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/sw-yx/react-typescript-cheatsheet/pull/14). The rest of this section just focuses on ESLint. From ff2e9659832fc82569cc54096f35d886a90ba68b Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 10 May 2019 00:23:59 -0400 Subject: [PATCH 087/791] Update ADVANCED.md --- ADVANCED.md | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index c7a4d982..868c4749 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -768,26 +768,29 @@ yarn add -D prettier husky lint-staged // inside package.json { - husky: { - hooks: { - 'pre-commit': 'lint-staged', - }, + //... + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } }, - 'lint-staged': { - linters: { - '*.{ts,tsx,js,jsx,css,scss,md}': [ - 'prettier --trailing-comma es5 --single-quote --write', - 'git add', + "lint-staged": { + "linters": { + "src/*.{ts,tsx,js,jsx,css,scss,md}": [ + "prettier --trailing-comma es5 --single-quote --write", + "git add" ], - ignore: ['**/dist/*, **/node_modules/*'], - }, - }, - prettier: { - printWidth: 80, - semi: true, - singleQuote: true, - trailingComma: 'es5', + "ignore": [ + "**/dist/*, **/node_modules/*" + ] + } }, + "prettier": { + "printWidth": 80, + "semi": false, + "singleQuote": true, + "trailingComma": "es5" + } } ``` From 11f3a87d0a8ed98968e87e33b7effe84f7afc324 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 10 May 2019 14:02:43 -0400 Subject: [PATCH 088/791] add eslint + TS guide --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 868c4749..a33d81cc 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -893,6 +893,8 @@ More `.eslintrc.json` options to consider with more options you may want for **a } ``` +You can read a [fuller TypeScript + ESLint setup guide here](https://github.com/MatterhornDev/matterhorn-posts/blob/learn-typescript-linting/learn-typescript-linting.md) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. + ## Working with Non-TypeScript Libraries (writing your own index.d.ts) Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: From 520c2c153366bd9c691ae887a4c55322dc1371a8 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 2 May 2019 16:56:09 +0200 Subject: [PATCH 089/791] Add prettier --- .gitignore | 1 + package.json | 8 ++++++-- yarn.lock | 8 ++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 yarn.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..40b878db --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/package.json b/package.json index d034df10..5016b1ad 100644 --- a/package.json +++ b/package.json @@ -1,9 +1,10 @@ { "name": "react-typescript-cheatsheet", "version": "1.0.0", - "description": "this package.json is just for all-contributors to work", + "description": "this package.json is just for maintenance work", "main": "index.js", "scripts": { + "format": "prettier --write \"**/*.md\"", "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { @@ -16,5 +17,8 @@ "bugs": { "url": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues" }, - "homepage": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#readme" + "homepage": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#readme", + "devDependencies": { + "prettier": "^1.17.0" + } } diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 00000000..0619ba57 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,8 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +prettier@^1.17.0: + version "1.17.0" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008" + integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw== From 9207c083e1e9dd33de99147e807187ce20f0fad5 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 2 May 2019 16:59:33 +0200 Subject: [PATCH 090/791] Use prettier config closest to current formatting --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 5016b1ad..bd16427b 100644 --- a/package.json +++ b/package.json @@ -20,5 +20,8 @@ "homepage": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#readme", "devDependencies": { "prettier": "^1.17.0" + }, + "prettier": { + "singleQuote": true } } From d87fced965562355634f5c0379d6802226d51a47 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 2 May 2019 16:59:37 +0200 Subject: [PATCH 091/791] Use prettier --- .github/ISSUE_TEMPLATE/advanced-cheatsheet.md | 5 +- .github/ISSUE_TEMPLATE/basic-cheatsheet.md | 5 +- .../general-react-ts-question.md | 5 +- .github/ISSUE_TEMPLATE/hoc-cheatsheet.md | 5 +- .../ISSUE_TEMPLATE/migrating-cheatsheet.md | 5 +- ADVANCED.md | 29 ++- CONTRIBUTORS.md | 4 +- MIGRATING.md | 3 +- README.md | 179 ++++++++++-------- 9 files changed, 121 insertions(+), 119 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md index 285145f3..91e8610b 100644 --- a/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md @@ -1,10 +1,9 @@ --- name: Advanced Cheatsheet about: Report Issue/Suggest an idea for Advanced Cheatsheet -title: "[Advanced] ISSUE_TITLE_HERE" +title: '[Advanced] ISSUE_TITLE_HERE' labels: ADVANCED assignees: '' - --- **What cheatsheet is this about? (if applicable)** @@ -13,4 +12,4 @@ Advanced cheatsheet **What's your issue or idea?** -*Write here* +_Write here_ diff --git a/.github/ISSUE_TEMPLATE/basic-cheatsheet.md b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md index 12a2df76..98cc7abc 100644 --- a/.github/ISSUE_TEMPLATE/basic-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md @@ -1,10 +1,9 @@ --- name: Basic Cheatsheet about: Report Issue/Suggest an idea for Basic Cheatsheet -title: "[Basic] ISSUE_TITLE_HERE" +title: '[Basic] ISSUE_TITLE_HERE' labels: BASIC assignees: sw-yx - --- **What cheatsheet is this about? (if applicable)** @@ -13,4 +12,4 @@ Basic cheatsheet **What's your issue or idea?** -*Write here* +_Write here_ diff --git a/.github/ISSUE_TEMPLATE/general-react-ts-question.md b/.github/ISSUE_TEMPLATE/general-react-ts-question.md index e73563b0..2984f346 100644 --- a/.github/ISSUE_TEMPLATE/general-react-ts-question.md +++ b/.github/ISSUE_TEMPLATE/general-react-ts-question.md @@ -1,10 +1,7 @@ --- name: General React+TS Question about: Questions are welcome! We want to know and solve your pain points. -title: "[Question] QUESTION_TITLE_HERE" +title: '[Question] QUESTION_TITLE_HERE' labels: good first issue assignees: sw-yx - --- - - diff --git a/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md index 9b6407fe..b7dde203 100644 --- a/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md @@ -1,10 +1,9 @@ --- name: HOC Cheatsheet about: Report Issue/Suggest an idea for HOC Cheatsheet -title: "[HOC] ISSUE_TITLE_HERE" +title: '[HOC] ISSUE_TITLE_HERE' labels: HOC assignees: '' - --- **What cheatsheet is this about? (if applicable)** @@ -13,4 +12,4 @@ HOC cheatsheet **What's your issue or idea?** -*Write here* +_Write here_ diff --git a/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md index 458f5c77..b2056d1c 100644 --- a/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md @@ -1,10 +1,9 @@ --- name: Migrating Cheatsheet about: Report Issue/Suggest an idea for Migrating Cheatsheet -title: "[Migrating] ISSUE_TITLE_HERE" +title: '[Migrating] ISSUE_TITLE_HERE' labels: MIGRATING assignees: '' - --- **What cheatsheet is this about? (if applicable)** @@ -13,4 +12,4 @@ Migrating cheatsheet **What's your issue or idea?** -*Write here* +_Write here_ diff --git a/ADVANCED.md b/ADVANCED.md index a33d81cc..8b657ba7 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -378,11 +378,11 @@ You want to allow `expanded` to be passed only if `truncate` is also passed, bec You can do this by function overloads: ```tsx -import React from "react"; +import React from 'react'; type CommonProps = { children: React.ReactNode; - as: "p" | "span" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; + as: 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; }; type NoTruncateProps = CommonProps & { @@ -403,18 +403,13 @@ const isTruncateProps = ( function Text(props: NoTruncateProps): JSX.Element; function Text(props: TruncateProps): JSX.Element; function Text(props: NoTruncateProps | TruncateProps) { - if (isTruncateProps(props)) { const { children, as: Tag, truncate, expanded, ...otherProps } = props; - const classNames = truncate ? ".truncate" : ""; + const classNames = truncate ? '.truncate' : ''; return ( - + {children} ); @@ -427,17 +422,20 @@ function Text(props: NoTruncateProps | TruncateProps) { Text.defaultProps = { as: 'span' -} +}; ``` Using the Text component: + ```tsx const App: React.FC = () => ( <> not truncated {/* works */} truncated {/* works */} - truncate-able but expanded {/* works */} - + + truncate-able but expanded + + {/* works */} {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type 'Pick'} */} truncate-able but expanded @@ -676,7 +674,7 @@ export function useLoading() { setState(true); return aPromise.finally(() => setState(false)); }; - return [isLoading, load] as const // infers [boolean, typeof load] instead of (boolean | typeof load)[] + return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] } ``` @@ -852,7 +850,6 @@ This is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/file More `.eslintrc.json` options to consider with more options you may want for **apps**: ```json - { "extends": [ "airbnb", @@ -910,8 +907,8 @@ So create a `.d.ts` file anywhere in your project with the module definition: ```ts // de-indent.d.ts declare module 'de-indent' { - function deindent(): void - export = deindent // default export + function deindent(): void; + export = deindent; // default export } ``` diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index ae7fd052..46670c0f 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,4 +1,3 @@ - ## Contributors @@ -6,10 +5,11 @@
Ferdy Budhidharma
Ferdy Budhidharma

👀 🚧 🖋
swyx
swyx

🤔 👀 🚧 🖋 💬
Sebastian Silbermann
Sebastian Silbermann

👀 🚧 🖋
Islam Attrash
Islam Attrash

🚧 🖋
Stephen Koo
Stephen Koo

💬 💡
+ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): -This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! \ No newline at end of file +This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome! diff --git a/MIGRATING.md b/MIGRATING.md index 0141850d..8980089a 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -54,7 +54,6 @@ Misc tips/approaches successful companies have taken - 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)) - 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]) -
@@ -109,7 +108,7 @@ Problems to be aware of: - If you have an error in the jsdoc, you get no warning/error. TS just silently doesn't type annotate the function. - [casting can be verbose](https://twitter.com/bahmutov/status/1089229349637754880) -(*thanks [Gil Tayar](https://twitter.com/giltayar/status/1089228919260221441) and [Gleb Bahmutov](https://twitter.com/bahmutov/status/1089229196247908353) for sharing above commentary*) +(_thanks [Gil Tayar](https://twitter.com/giltayar/status/1089228919260221441) and [Gleb Bahmutov](https://twitter.com/bahmutov/status/1089229196247908353) for sharing above commentary_) ## From JS diff --git a/README.md b/README.md index 0dec2c34..6f5888c4 100644 --- a/README.md +++ b/README.md @@ -247,8 +247,8 @@ setUser(newUser); When using `useRef`, you have two options when creating a ref container that does not have an initial value: ```ts -const ref1 = useRef(null!) -const ref2 = useRef(null) +const ref1 = useRef(null!); +const ref2 = useRef(null); ``` The first option will make `ref1.current` read-only, 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). @@ -263,8 +263,14 @@ When using `useEffect`, take care not to return anything other than a function o function DelayedEffect(props: { timerMs: number }) { const { timerMs } = props; // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces - useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) - return null + useEffect( + () => + setTimeout(() => { + /* do stuff */ + }, timerMs), + [timerMs] + ); + return null; } ``` @@ -278,13 +284,13 @@ function TextInputWithFocusButton() { // 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) { + if (inputEl && inputEl.current) { inputEl.current.focus(); - } + } }; return ( <> - { /* in addition, inputEl only can be used with input elements. Yay! */ } + {/* in addition, inputEl only can be used with input elements. Yay! */} @@ -300,8 +306,8 @@ You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/ ```tsx type Action = - { type: 'SET_ONE'; payload: string; } - | { type: 'SET_TWO'; payload: number; }; + | { type: 'SET_ONE'; payload: string } + | { type: 'SET_TWO'; payload: number }; export function reducer(state: AppState, action: Action): AppState { switch (action.type) { @@ -325,7 +331,6 @@ export function reducer(state: AppState, action: Action): AppState { If you are returning an array in your Custom Hook, you will want to avoid type inference as Typescript will infer a union type (when you actually want different types in each position of the array). Instead, use [TS 3.4 const assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions): - ```tsx export function useLoading() { const [isLoading, setState] = React.useState(false); @@ -333,9 +338,9 @@ export function useLoading() { setState(true); return aPromise.finally(() => setState(false)); }; - return [isLoading, load] as const // infers [boolean, typeof load] instead of (boolean | typeof load)[] + return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] } -``` +``` This way, when you destructure you actually get the right types based on destructure position. @@ -356,23 +361,25 @@ export function useLoading() { (aPromise: Promise) => Promise ]; } -``` - +``` A helper function that automatically types tuples can also be helpful if you write a lot of custom hooks: + ```ts -function tuplify(...elements: T) { return elements } +function tuplify(...elements: T) { + return elements; +} function useArray() { - const numberValue = useRef(3).current - const functionValue = useRef(() => {}).current - return [numberValue, functionValue] // type is (number | (() => void))[] + const numberValue = useRef(3).current; + const functionValue = useRef(() => {}).current; + return [numberValue, functionValue]; // type is (number | (() => void))[] } function useTuple() { - const numberValue = useRef(3).current - const functionValue = useRef(() => {}).current - return tuplify(numberValue, functionValue) // type is [number, () => void] + const numberValue = useRef(3).current; + const functionValue = useRef(() => {}).current; + return tuplify(numberValue, functionValue); // type is [number, () => void] } ``` @@ -506,42 +513,43 @@ For Typescript 3.0+, type inference [should work](https://www.typescriptlang.org // //////////////// type Props = { age: number } & typeof defaultProps; const defaultProps = { - who: 'Johny Five', + who: 'Johny Five' }; const Greet = (props: Props) => { /*...*/ }; -Greet.defaultProps = defaultProps +Greet.defaultProps = defaultProps; ``` For **Class components**, there are [a couple ways to do it](https://github.com/sw-yx/react-typescript-cheatsheet/pull/103#issuecomment-481061483)(including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: ```tsx type GreetProps = typeof Greet.defaultProps & { - age: number -} + age: number; +}; class Greet extends React.Component { static defaultProps = { name: 'world' - } + }; /*...*/ } // Type-checks! No type assertions needed! let el = ; ``` +
Why does React.FC break defaultProps? - You can check the discussions here: +You can check the discussions here: - - https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 - - https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 - - https://github.com/sw-yx/react-typescript-cheatsheet/issues/87 +- https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 +- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 +- https://github.com/sw-yx/react-typescript-cheatsheet/issues/87 - This is just the current state and may be fixed in future. +This is just the current state and may be fixed in future.
@@ -677,7 +685,8 @@ If performance is not an issue, inlining handlers is easiest as you can just use ```tsx const el = ( -
- [Something to add? File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new). ## forwardRef/createRef - Check the [Hooks section](https://github.com/sw-yx/react-typescript-cheatsheet/blob/master/README.md) for `useRef`. `createRef`: @@ -917,7 +929,6 @@ export const FancyButton = React.forwardRef((props, ref) => ( )); ``` - If you are grabbing the props of a component that forwards refs, use [`ComponentPropsWithRef`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts#L735). More info: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 @@ -1188,6 +1199,7 @@ partialStateUpdate({ foo: 2 }); // this works Note that there are some TS users who don't agree with using `Partial` as it behaves today. See [subtle pitfalls of the above example here](https://twitter.com/ferdaber/status/1084798596027957248), and check out this long discussion on [why @types/react uses Pick instead of Partial](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18365). + ## The Types I need weren't exported! @@ -1218,6 +1230,7 @@ function foo(bar: string) { // inside your app, if you need { baz: number } type FooReturn = ReturnType; // { baz: number } ``` + # Troubleshooting Handbook: Images and other non-TS/TSX files Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): @@ -1225,10 +1238,10 @@ Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declarati ```ts // declaration.d.ts // anywhere in your project, NOT the same name as any of your .ts/tsx files -declare module '*.png' +declare module '*.png'; // importing in a tsx file -import * as logo from "./logo.png"; +import * as logo from './logo.png'; ``` Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 and [StackOverflow](https://stackoverflow.com/a/49715468/4216035) From f0b3cfd60f1459c1ec54b2d27289eb7f856ff269 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 22:11:04 +0200 Subject: [PATCH 092/791] Fix syntax error --- HOC.md | 50 ++++++++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/HOC.md b/HOC.md index 6ea5a740..3b07db72 100644 --- a/HOC.md +++ b/HOC.md @@ -356,33 +356,38 @@ function Dog({name, owner}: DogProps) { And we have a `withOwner` HOC that injects the `owner`: ```tsx -const OwnedDog = withOwner('swyx')(Dog) +const OwnedDog = withOwner('swyx')(Dog); ``` 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: ```tsx -typeof OwnedDog // we want this to be equal to { name: string } +typeof OwnedDog; // we want this to be equal to { name: string } - // this should be fine - // this should have a typeError - // this should be fine +; // this should be fine +; // this should have a typeError +; // this should be fine // and the HOC should be reusable for completely different prop types! -type CatProps { - lives: number - owner: string -} -function Cat({lives, owner}: CatProps) { - return
Meow: {lives}, Owner: {owner}
+type CatProps = { + lives: number; + owner: string; +}; +function Cat({ lives, owner }: CatProps) { + return ( +
+ {' '} + Meow: {lives}, Owner: {owner} +
+ ); } -const OwnedCat = withOwner('swyx')(Cat) +const OwnedCat = withOwner('swyx')(Cat); - // this should be fine - // this should have a typeError - // this should be fine +; // this should be fine +; // this should have a typeError +; // this should be fine ``` So how do we type `withOwner`? @@ -396,16 +401,17 @@ So how do we type `withOwner`? ```tsx function withOwner(owner: string) { - return function (Component: React.ComponentType) { - return function (props: Omit): React.ReactNode { - return - } - } + return function( + Component: React.ComponentType + ) { + return function(props: Omit): React.ReactNode { + return ; + }; + }; } ``` -*Note: above is an incomplete, nonworking example. PR a fix!* - +_Note: above is an incomplete, nonworking example. PR a fix!_ ## Good articles From c1b58924e96b059650c0936f4f11e88da4211d7c Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 22:12:43 +0200 Subject: [PATCH 093/791] Format rebase --- ADVANCED.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 8b657ba7..74bcf44f 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -890,7 +890,7 @@ More `.eslintrc.json` options to consider with more options you may want for **a } ``` -You can read a [fuller TypeScript + ESLint setup guide here](https://github.com/MatterhornDev/matterhorn-posts/blob/learn-typescript-linting/learn-typescript-linting.md) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. +You can read a [fuller TypeScript + ESLint setup guide here](https://github.com/MatterhornDev/matterhorn-posts/blob/learn-typescript-linting/learn-typescript-linting.md) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. ## Working with Non-TypeScript Libraries (writing your own index.d.ts) diff --git a/README.md b/README.md index 6f5888c4..5a0c867b 100644 --- a/README.md +++ b/README.md @@ -1149,7 +1149,7 @@ export const Human: React.FC = // ... export const Dog: React.FC = // ... ``` -Make sure not to confuse Intersection Types (which are **and** operations) with Union Types (which are **or** operations). +Make sure not to confuse Intersection Types (which are **and** operations) with Union Types (which are **or** operations). ## Union Types From 5a219c492e8a5e32ec64cbc7cedf818440f91873 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 21:00:30 +0200 Subject: [PATCH 094/791] Consistently access React namspace instead of imports --- ADVANCED.md | 6 +++--- HOC.md | 4 ++-- README.md | 20 ++++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index a33d81cc..fecfbeed 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -99,10 +99,10 @@ The goal is to have the props available on the interface for the component, but ```ts interface Props extends WithThemeProps { - children: ReactNode; + children: React.ReactNode; } -class MyButton extends Component { +class MyButton extends React.Component { public render() { // Render an the element using the theme and other props. } @@ -207,7 +207,7 @@ export interface Props { `ElementType` is pretty useful to cover most types that can be passed to createElement e.g. ```tsx -function PassThrough(props: { as: ElementType }) { +function PassThrough(props: { as: React.ElementType }) { const { as: Component } = props; return ; diff --git a/HOC.md b/HOC.md index 6ea5a740..41058bb2 100644 --- a/HOC.md +++ b/HOC.md @@ -137,7 +137,7 @@ interface WithDataProps { // C is the actual interface of the wrapped component (used to grab defaultProps from it) export function withSubscription, C>( // this type allows us to infer P, but grab the type of WrappedComponent separately without it interfering with the inference of P - WrappedComponent: JSXElementConstructor

& C, + WrappedComponent: React.JSXElementConstructor

& C, // selectData is a functor for T // props is Readonly because it's readonly inside of the class selectData: ( @@ -151,7 +151,7 @@ export function withSubscription, C>( type State = { data: T; }; - return class WithData extends Component { + return class WithData extends React.Component { constructor(props: Props) { super(props); this.handleChange = this.handleChange.bind(this); diff --git a/README.md b/README.md index 0dec2c34..053a4948 100644 --- a/README.md +++ b/README.md @@ -228,7 +228,7 @@ Hooks are [supported in `@types/react` from v16.8 up](https://github.com/Definit Type inference works very well most of the time: ```tsx -const [val, toggle] = useState(false); // `val` is inferred to be a boolean, `toggle` only takes booleans +const [val, toggle] = React.useState(false); // `val` is inferred to be a boolean, `toggle` only takes booleans ``` See also the [Using Inferred Types](#using-inferred-types) section if you need to use a complex type that you've relied on inference for. @@ -236,7 +236,7 @@ See also the [Using Inferred Types](#using-inferred-types) section if you need t However, many hooks are initialized with null-ish default values, and you may wonder how to provide types. Explicitly declare the type, and use a union type: ```tsx -const [user, setUser] = useState(null); +const [user, setUser] = React.useState(null); // later... setUser(newUser); @@ -247,8 +247,8 @@ setUser(newUser); When using `useRef`, you have two options when creating a ref container that does not have an initial value: ```ts -const ref1 = useRef(null!) -const ref2 = useRef(null) +const ref1 = React.useRef(null!) +const ref2 = React.useRef(null) ``` The first option will make `ref1.current` read-only, 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). @@ -263,7 +263,7 @@ When using `useEffect`, take care not to return anything other than a function o function DelayedEffect(props: { timerMs: number }) { const { timerMs } = props; // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces - useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) + React.useEffect(() => setTimeout(() => {/* do stuff */}, timerMs), [timerMs]) return null } ``` @@ -273,7 +273,7 @@ function DelayedEffect(props: { timerMs: number }) { ```tsx function TextInputWithFocusButton() { // initialise with null, but tell TypeScript we are looking for an HTMLInputElement - const inputEl = useRef(null); + const inputEl = React.useRef(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 @@ -364,14 +364,14 @@ A helper function that automatically types tuples can also be helpful if you wri function tuplify(...elements: T) { return elements } function useArray() { - const numberValue = useRef(3).current - const functionValue = useRef(() => {}).current + const numberValue = React.useRef(3).current + const functionValue = React.useRef(() => {}).current return [numberValue, functionValue] // type is (number | (() => void))[] } function useTuple() { - const numberValue = useRef(3).current - const functionValue = useRef(() => {}).current + const numberValue = React.useRef(3).current + const functionValue = React.useRef(() => {}).current return tuplify(numberValue, functionValue) // type is [number, () => void] } ``` From bda743b337c647b6bbe0ea7ef8675a7861fa6dec Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 21:10:03 +0200 Subject: [PATCH 095/791] Fix incorrect syntax highlighting --- ADVANCED.md | 6 +++--- README.md | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index fecfbeed..f7bb0ddf 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -128,7 +128,7 @@ Now when consuming the component you can omit the `primaryColor` prop or overrid The actual HoC. -```ts +```tsx export function withTheme( WrappedComponent: React.ComponentType ) { @@ -157,7 +157,7 @@ Note that the `{...this.props as T}` assertion is needed because of a current bu 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: -```ts +```tsx // inject static values to a component so that they're always provided export function inject( Component: React.JSXElementConstructor, @@ -581,7 +581,7 @@ foo('hello', 'world'); // also works 2. Support for `propTypes` and `static defaultProps` in JSX using `LibraryManagedAttributes`: -```ts +```tsx export interface Props { name: string; } diff --git a/README.md b/README.md index 053a4948..016c2f6c 100644 --- a/README.md +++ b/README.md @@ -1002,7 +1002,7 @@ class App extends React.Component< **Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: -```tsx +```ts interface Admin { role: string: } @@ -1021,7 +1021,7 @@ function redirect(usr: Admin | User) { // Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough function isAdmin(usr: Admin | User): usr is Admin { - return (usr).role !==undefined + return (usr).role !== undefined } ``` From 902f275dd70b5e60b89f197f9247dbf0824fe3d1 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 21:45:34 +0200 Subject: [PATCH 096/791] Remove redundant overloads --- ADVANCED.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index f7bb0ddf..5dfb4523 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -233,8 +233,6 @@ function isPropsForAnchorElement( return 'href' in props; } -function Clickable(props: ButtonProps): JSX.Element; -function Clickable(props: AnchorProps): JSX.Element; function Clickable(props: ButtonProps | AnchorProps) { if (isPropsForAnchorElement(props)) { return ; @@ -249,8 +247,6 @@ They don't even need to be completely different props, as long as they have at l ```tsx type LinkProps = Omit & { to?: string }; -function RouterLink(props: LinkProps): JSX.Element; -function RouterLink(props: AnchorProps): JSX.Element; function RouterLink(props: LinkProps | AnchorProps) { if ('to' in props) { return ; @@ -333,8 +329,6 @@ Use the `in` keyword, function overloading, and union types to make components t type Props1 = { foo: string }; type Props2 = { bar: string }; -function MyComponent(props: Props1): JSX.Element; -function MyComponent(props: Props2): JSX.Element; function MyComponent(props: Props1 | Props2) { if ('foo' in props) { // props.bar // error @@ -400,8 +394,6 @@ const isTruncateProps = ( ): props is TruncateProps => !!props.truncate; // Function overloads to accept both prop types NoTruncateProps & TruncateProps -function Text(props: NoTruncateProps): JSX.Element; -function Text(props: TruncateProps): JSX.Element; function Text(props: NoTruncateProps | TruncateProps) { if (isTruncateProps(props)) { From 2fd9a098c74f03cc25ddf54ee804184130343afd Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 21:51:21 +0200 Subject: [PATCH 097/791] Fix Type 'C' does not satisfy the constraint in ApparentProps --- ADVANCED.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 5dfb4523..2ec64a9f 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -480,7 +480,9 @@ The advantage of extracting the prop types is that you won't need to export ever import { ComponentProps, JSXElementConstructor } from 'react'; // goes one step further and resolves with propTypes and defaultProps properties -type ApparentComponentProps = C extends JSXElementConstructor +type ApparentComponentProps< + C extends keyof JSX.IntrinsicElements | JSXElementConstructor +> = C extends JSXElementConstructor ? JSX.LibraryManagedAttributes : ComponentProps; ``` From d5c1576814c0a18d87414a00e9faf2ff20a2ab2e Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 14 May 2019 22:02:56 +0200 Subject: [PATCH 098/791] Fix duplicate identifier --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 016c2f6c..87c7177f 100644 --- a/README.md +++ b/README.md @@ -651,7 +651,7 @@ export declare interface AppProps { children1: JSX.Element; // bad, doesnt account for arrays children2: JSX.Element | JSX.Element[]; // meh, doesnt accept functions children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility - children3: React.ReactChild[]; // better + children4: React.ReactChild[]; // better children: React.ReactNode; // best, accepts everything functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type style?: React.CSSProperties; // to pass through style props From 6ce641d919ac5531d7310b671371c2f3bcc1aeac Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Wed, 15 May 2019 11:53:44 +0200 Subject: [PATCH 099/791] Use `as` cast instead of bracket cast --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 87c7177f..094302b8 100644 --- a/README.md +++ b/README.md @@ -1021,7 +1021,7 @@ function redirect(usr: Admin | User) { // Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough function isAdmin(usr: Admin | User): usr is Admin { - return (usr).role !== undefined + return (usr as any).role !== undefined } ``` From 3579039619ed249983ca81e2182d4763369ac2e3 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Wed, 15 May 2019 11:54:07 +0200 Subject: [PATCH 100/791] usr -> user --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 094302b8..3ab0e89c 100644 --- a/README.md +++ b/README.md @@ -1011,17 +1011,17 @@ interface User { } // Method 1: use `in` keyword -function redirect(usr: Admin | User) { - if("role" in usr) { // use the `in` operator for typeguards since TS 2.7+ - routeToAdminPage(usr.role); +function redirect(user: Admin | User) { + if("role" in user) { // use the `in` operator for typeguards since TS 2.7+ + routeToAdminPage(user.role); } else { - routeToHomePage(usr.email); + routeToHomePage(user.email); } } // Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough -function isAdmin(usr: Admin | User): usr is Admin { - return (usr as any).role !== undefined +function isAdmin(user: Admin | User): user is Admin { + return (user as any).role !== undefined } ``` From f9b0818237930d0febf7a5ffffff1c02f9fbe8df Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 16 May 2019 12:13:07 -0400 Subject: [PATCH 101/791] Update ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index a33d81cc..4ac28bd6 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -49,7 +49,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Higher Order Components](#higher-order-components-hocs) - [Render Props](#render-props) - [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered) - - [Types for Conditional Rendering](#types-for-conditional-rendering) + - [Typing a Component that Accepts Different Props](#typing-a-component-that-accepts-different-props) - [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both) - [Props: Must Pass Both](#props-must-pass-both) - [Props: Can Optionally Pass One Only If the Other Is Passed](#props-can-optionally-pass-one-only-if-the-other-is-passed) From 82ade81f87526cec33a317e45771769b2da4f4cf Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:21 +0000 Subject: [PATCH 102/791] Prettify .github/ISSUE_TEMPLATE/advanced-cheatsheet.md --- .github/ISSUE_TEMPLATE/advanced-cheatsheet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md index 91e8610b..659f19fa 100644 --- a/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/advanced-cheatsheet.md @@ -1,9 +1,9 @@ --- name: Advanced Cheatsheet about: Report Issue/Suggest an idea for Advanced Cheatsheet -title: '[Advanced] ISSUE_TITLE_HERE' +title: "[Advanced] ISSUE_TITLE_HERE" labels: ADVANCED -assignees: '' +assignees: "" --- **What cheatsheet is this about? (if applicable)** From f2f2e7e084026d142349a6938fedfc9c052c1012 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:22 +0000 Subject: [PATCH 103/791] Prettify .github/ISSUE_TEMPLATE/basic-cheatsheet.md --- .github/ISSUE_TEMPLATE/basic-cheatsheet.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/basic-cheatsheet.md b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md index 98cc7abc..c5a39477 100644 --- a/.github/ISSUE_TEMPLATE/basic-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/basic-cheatsheet.md @@ -1,7 +1,7 @@ --- name: Basic Cheatsheet about: Report Issue/Suggest an idea for Basic Cheatsheet -title: '[Basic] ISSUE_TITLE_HERE' +title: "[Basic] ISSUE_TITLE_HERE" labels: BASIC assignees: sw-yx --- From 7840951aac7d62f0e1198200cf91cab34b765325 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:23 +0000 Subject: [PATCH 104/791] Prettify .github/ISSUE_TEMPLATE/hoc-cheatsheet.md --- .github/ISSUE_TEMPLATE/hoc-cheatsheet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md index b7dde203..76c46a9a 100644 --- a/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/hoc-cheatsheet.md @@ -1,9 +1,9 @@ --- name: HOC Cheatsheet about: Report Issue/Suggest an idea for HOC Cheatsheet -title: '[HOC] ISSUE_TITLE_HERE' +title: "[HOC] ISSUE_TITLE_HERE" labels: HOC -assignees: '' +assignees: "" --- **What cheatsheet is this about? (if applicable)** From 374fc72b60ffadd0d05a37723b476fd245e32607 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:24 +0000 Subject: [PATCH 105/791] Prettify .github/ISSUE_TEMPLATE/migrating-cheatsheet.md --- .github/ISSUE_TEMPLATE/migrating-cheatsheet.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md index b2056d1c..2a3dde11 100644 --- a/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md +++ b/.github/ISSUE_TEMPLATE/migrating-cheatsheet.md @@ -1,9 +1,9 @@ --- name: Migrating Cheatsheet about: Report Issue/Suggest an idea for Migrating Cheatsheet -title: '[Migrating] ISSUE_TITLE_HERE' +title: "[Migrating] ISSUE_TITLE_HERE" labels: MIGRATING -assignees: '' +assignees: "" --- **What cheatsheet is this about? (if applicable)** From 0d3af1c5ced872616a595f5d9b1cf0a496892fbd Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:25 +0000 Subject: [PATCH 106/791] Prettify .github/ISSUE_TEMPLATE/general-react-ts-question.md --- .github/ISSUE_TEMPLATE/general-react-ts-question.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/general-react-ts-question.md b/.github/ISSUE_TEMPLATE/general-react-ts-question.md index 2984f346..da18488a 100644 --- a/.github/ISSUE_TEMPLATE/general-react-ts-question.md +++ b/.github/ISSUE_TEMPLATE/general-react-ts-question.md @@ -1,7 +1,7 @@ --- name: General React+TS Question about: Questions are welcome! We want to know and solve your pain points. -title: '[Question] QUESTION_TITLE_HERE' +title: "[Question] QUESTION_TITLE_HERE" labels: good first issue assignees: sw-yx --- From 0a963e96a62fdfe013e467374fdd68b664517812 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:27 +0000 Subject: [PATCH 107/791] Prettify HOC.md --- HOC.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/HOC.md b/HOC.md index 3b07db72..8414ce6d 100644 --- a/HOC.md +++ b/HOC.md @@ -67,15 +67,15 @@ const TextBlock = Comment; type CommentType = { text: string; id: number }; const comments: CommentType[] = [ { - text: 'comment1', + text: "comment1", id: 1 }, { - text: 'comment2', + text: "comment2", id: 2 } ]; -const blog = 'blogpost'; +const blog = "blogpost"; /** mock data source */ const DataSource = { @@ -142,12 +142,12 @@ export function withSubscription, C>( // props is Readonly because it's readonly inside of the class selectData: ( dataSource: typeof DataSource, - props: Readonly>> + props: Readonly>> ) => T ) { // the magic is here: JSX.LibraryManagedAttributes will take the type of WrapedComponent and resolve its default props // against the props of WithData, which is just the original P type with 'data' removed from its requirements - type Props = JSX.LibraryManagedAttributes>; + type Props = JSX.LibraryManagedAttributes>; type State = { data: T; }; @@ -187,7 +187,7 @@ export const CommentListWithSubscription = withSubscription( export const BlogPostWithSubscription = withSubscription( BlogPost, - (DataSource: DataType, props: Omit) => + (DataSource: DataType, props: Omit) => DataSource.getBlogPost(props.id) ); ``` @@ -202,8 +202,8 @@ function logProps(WrappedComponent: React.ComponentType) { componentWillReceiveProps( nextProps: React.ComponentProps ) { - console.log('Current props: ', this.props); - console.log('Next props: ', nextProps); + console.log("Current props: ", this.props); + console.log("Next props: ", nextProps); } render() { // Wraps the input component in a container, without mutating it. Good! @@ -239,11 +239,11 @@ type Omit = Pick>; type CommentType = { text: string; id: number }; const comments: CommentType[] = [ { - text: 'comment1', + text: "comment1", id: 1 }, { - text: 'comment2', + text: "comment2", id: 2 } ]; @@ -284,7 +284,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { return function, C>( WrappedComponent: React.ComponentType ) { - type Props = JSX.LibraryManagedAttributes>; + type Props = JSX.LibraryManagedAttributes>; // Creating the inner component. The calculated Props type here is the where the magic happens. return class ComponentWithTheme extends React.Component { public render() { @@ -327,7 +327,7 @@ function withSubscription< } function getDisplayName(WrappedComponent: React.ComponentType) { - return WrappedComponent.displayName || WrappedComponent.name || 'Component'; + return WrappedComponent.displayName || WrappedComponent.name || "Component"; } ``` @@ -356,7 +356,7 @@ function Dog({name, owner}: DogProps) { And we have a `withOwner` HOC that injects the `owner`: ```tsx -const OwnedDog = withOwner('swyx')(Dog); +const OwnedDog = withOwner("swyx")(Dog); ``` 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: @@ -377,13 +377,13 @@ type CatProps = { function Cat({ lives, owner }: CatProps) { return (

); } -const OwnedCat = withOwner('swyx')(Cat); +const OwnedCat = withOwner("swyx")(Cat); ; // this should be fine ; // this should have a typeError @@ -404,7 +404,7 @@ function withOwner(owner: string) { return function( Component: React.ComponentType ) { - return function(props: Omit): React.ReactNode { + return function(props: Omit): React.ReactNode { return ; }; }; From 1700f723cf4c58e1074c8a11875187f01ea60c3e Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:19:29 +0000 Subject: [PATCH 108/791] Prettify MIGRATING.md --- MIGRATING.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/MIGRATING.md b/MIGRATING.md index 8980089a..7c0311d3 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -158,10 +158,10 @@ Old content that is possibly out of date ## Links -[hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 'Thoughts on migrating to TypeScript' -[clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 'Incrementally Migrating JavaScript to TypeScript' -[pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 'Migrating a Babel project to TypeScript' -[mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide 'TypeScript React Conversion Guide' -[entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d 'Incremental Migration to TypeScript on a Flowtype codebase' -[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' -[tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ 'Benefits of gradual strong typing in JavaScript' +[hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" +[clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 "Incrementally Migrating JavaScript to TypeScript" +[pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 "Migrating a Babel project to TypeScript" +[mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide "TypeScript React Conversion Guide" +[entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" +[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" +[tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ "Benefits of gradual strong typing in JavaScript" From d758c523e8952ac42e878c9aef1e6f18e976a554 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Thu, 16 May 2019 16:27:19 +0000 Subject: [PATCH 109/791] Prettify ADVANCED.md --- ADVANCED.md | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 68c131f8..ef16547e 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -134,7 +134,7 @@ export function withTheme( ) { // Try to create a nice displayName for React Dev Tools. const displayName = - WrappedComponent.displayName || WrappedComponent.name || 'Component'; + WrappedComponent.displayName || WrappedComponent.name || "Component"; // Creating the inner component. The calculated Props type here is the where the magic happens. return class ComponentWithTheme extends React.Component< @@ -223,14 +223,14 @@ Components, and JSX in general, are analogous to functions. When a component can A very common use case for this is to render something as either a button or an anchor, based on if it receives a `href` attribute. ```tsx -type ButtonProps = JSX.IntrinsicElements['button']; -type AnchorProps = JSX.IntrinsicElements['a']; +type ButtonProps = JSX.IntrinsicElements["button"]; +type AnchorProps = JSX.IntrinsicElements["a"]; // optionally use a custom type guard function isPropsForAnchorElement( props: ButtonProps | AnchorProps ): props is AnchorProps { - return 'href' in props; + return "href" in props; } function Clickable(props: ButtonProps | AnchorProps) { @@ -245,10 +245,10 @@ function Clickable(props: ButtonProps | AnchorProps) { They don't even need to be completely different props, as long as they have at least one difference in properties: ```tsx -type LinkProps = Omit & { to?: string }; +type LinkProps = Omit & { to?: string }; function RouterLink(props: LinkProps | AnchorProps) { - if ('to' in props) { + if ("to" in props) { return ; } else { return ; @@ -330,7 +330,7 @@ type Props1 = { foo: string }; type Props2 = { bar: string }; function MyComponent(props: Props1 | Props2) { - if ('foo' in props) { + if ("foo" in props) { // props.bar // error return
{props.foo}
; } else { @@ -356,9 +356,9 @@ type OneOrAnother = type Props = OneOrAnother<{ a: string; b: string }, {}>; -const a: Props = { a: 'a' }; // error -const b: Props = { b: 'b' }; // error -const ab: Props = { a: 'a', b: 'b' }; // ok +const a: Props = { a: "a" }; // error +const b: Props = { b: "b" }; // error +const ab: Props = { a: "a", b: "b" }; // ok ``` Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) @@ -372,11 +372,11 @@ You want to allow `expanded` to be passed only if `truncate` is also passed, bec You can do this by function overloads: ```tsx -import React from 'react'; +import React from "react"; type CommonProps = { children: React.ReactNode; - as: 'p' | 'span' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; + as: "p" | "span" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; }; type NoTruncateProps = CommonProps & { @@ -398,7 +398,7 @@ function Text(props: NoTruncateProps | TruncateProps) { if (isTruncateProps(props)) { const { children, as: Tag, truncate, expanded, ...otherProps } = props; - const classNames = truncate ? '.truncate' : ''; + const classNames = truncate ? ".truncate" : ""; return ( @@ -413,7 +413,7 @@ function Text(props: NoTruncateProps | TruncateProps) { } Text.defaultProps = { - as: 'span' + as: "span" }; ``` @@ -447,7 +447,7 @@ type Omit = Pick>; // usage export const Checkbox = ( - props: Props & Omit, 'label'> + props: Props & Omit, "label"> ) => { const { label } = props; return ( @@ -475,7 +475,7 @@ so you can either export the props type as part of the module or extract them (e The advantage of extracting the prop types is that you won't need to export everything, and a refactor of the source of truth component will propagate to all consuming components. ```ts -import { ComponentProps, JSXElementConstructor } from 'react'; +import { ComponentProps, JSXElementConstructor } from "react"; // goes one step further and resolves with propTypes and defaultProps properties type ApparentComponentProps< @@ -502,7 +502,7 @@ export function MyInnerComponent(props: { // my-consuming-component.tsx export function MyConsumingComponent() { // event and moreArgs are contextually typed along with the return value - const theHandler: Props['onSomeEvent'] = ( + const theHandler: Props["onSomeEvent"] = ( event, moreArgs ) => {}; @@ -533,8 +533,8 @@ export interface InputFormProps { export const InputForm = styledInput` color: - ${({ themeName }) => (themeName === 'dark' ? 'black' : 'white')}; - border-color: ${({ foo }) => (foo ? 'red' : 'black')}; + ${({ themeName }) => (themeName === "dark" ? "black" : "white")}; + border-color: ${({ foo }) => (foo ? "red" : "black")}; `; ``` @@ -567,8 +567,8 @@ function foo(...rest: string[]) { // ... } -foo('hello'); // works -foo('hello', 'world'); // also works +foo("hello"); // works +foo("hello", "world"); // also works ``` 2. Support for `propTypes` and `static defaultProps` in JSX using `LibraryManagedAttributes`: @@ -583,7 +583,7 @@ export class Greet extends React.Component { const { name } = this.props; return
Hello ${name.toUpperCase()}!
; } - static defaultProps = { name: 'world' }; + static defaultProps = { name: "world" }; } // Type-checks! No type assertions needed! @@ -618,7 +618,7 @@ let service2: IDataService2; const response2 = service2.getData(); // response2.a.b.c.d; // COMPILE TIME ERROR if you do this -if (typeof response === 'string') { +if (typeof response === "string") { console.log(response.toUpperCase()); // `response` now has type 'string' } ``` @@ -705,7 +705,7 @@ export class MyComponent extends React.Component { 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/documentation/components-api#propstable)). The main thing to remember is to use `/** YOUR_COMMENT_HERE */` syntax in the line just above whatever you're annotating. ```tsx -import React from 'react'; +import React from "react"; interface MyProps { /** Description of prop "label". @@ -717,7 +717,7 @@ interface MyProps { /** * General component description in JSDoc format. Markdown is *supported*. */ -export default function MyComponent({ label = 'foobar' }: MyProps) { +export default function MyComponent({ label = "foobar" }: MyProps) { return
Hello world {label}
; } ``` @@ -900,7 +900,7 @@ So create a `.d.ts` file anywhere in your project with the module definition: ```ts // de-indent.d.ts -declare module 'de-indent' { +declare module "de-indent" { function deindent(): void; export = deindent; // default export } From c54cf261dee7761c59e5f9482f0bad8310b41d5a Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 22 May 2019 18:32:39 -0400 Subject: [PATCH 110/791] Update ADVANCED.md --- ADVANCED.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index ef16547e..c2410e05 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -672,6 +672,33 @@ export function useLoading() { } ``` +## TypeScript 3.5 + +[RC](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5-rc/) + +1. Built-in `` Type + +2. Higher order type inference from generic constructors + +``` +type ComponentClass

= new (props: P) => Component

; +declare class Component

{ + props: P; + constructor(props: P); +} + +declare function myHoc

(C: ComponentClass

): ComponentClass

; + +type NestedProps = { foo: number, stuff: T }; + +declare class GenericComponent extends Component> { +} + +// type is 'new (props: NestedProps) => Component>' +const GenericComponent2 = myHoc(GenericComponent); + +``` + ## TypeScript Roadmap https://github.com/Microsoft/TypeScript/wiki/Roadmap From a2443154a329df35b180d80c59c3443b7d6488b1 Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 25 May 2019 17:05:28 -0400 Subject: [PATCH 111/791] add const assertions article --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index c2410e05..eb28df64 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -672,6 +672,8 @@ export function useLoading() { } ``` +More info on places you can use [const assertions](https://blog.logrocket.com/const-assertions-are-the-killer-new-typescript-feature-b73451f35802). + ## TypeScript 3.5 [RC](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5-rc/) From 36f44d6829c8bde58f88bea7d38d81eaf7a1035d Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 29 May 2019 13:34:36 -0400 Subject: [PATCH 112/791] Update ADVANCED.md --- ADVANCED.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index eb28df64..799e1609 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -443,6 +443,7 @@ export interface Props { label: React.ReactNode; // this will conflict with the InputElement's label } +// this comes inbuilt with TS 3.5 type Omit = Pick>; // usage @@ -676,9 +677,9 @@ More info on places you can use [const assertions](https://blog.logrocket.com/co ## TypeScript 3.5 -[RC](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5-rc/) +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/)] -1. Built-in `` Type +1. Built-in `` Type!! 2. Higher order type inference from generic constructors From 5454df95ce42620a6854ee5524e978319a5dff25 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 3 Jun 2019 16:26:49 -0400 Subject: [PATCH 113/791] Update ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 799e1609..b61686d5 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -635,7 +635,7 @@ You can also assert a type, or use a **type guard** against an `unknown` type. T Attaching properties to functions like this "just works" now: ```tsx -export const FooComponent => ({ name }) => ( +export const FooComponent = ({ name }) => (

Hello! I am {name}
); From 495142a4c37a04aad4985a67acadd2b6f5717350 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" Date: Mon, 3 Jun 2019 20:26:53 +0000 Subject: [PATCH 114/791] Prettify ADVANCED.md --- ADVANCED.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index b61686d5..5b05f34e 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -635,12 +635,10 @@ You can also assert a type, or use a **type guard** against an `unknown` type. T Attaching properties to functions like this "just works" now: ```tsx -export const FooComponent = ({ name }) => ( -
Hello! I am {name}
-); +export const FooComponent = ({ name }) =>
Hello! I am {name}
; FooComponent.defaultProps = { - name: "swyx", + name: "swyx" }; ``` From 5523471352350d2ead7e99a63c3a2c56f4c1cbd8 Mon Sep 17 00:00:00 2001 From: swyx Date: Sun, 16 Jun 2019 10:46:43 -0400 Subject: [PATCH 115/791] Update MIGRATING.md --- MIGRATING.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MIGRATING.md b/MIGRATING.md index 7c0311d3..aa2130a4 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -120,6 +120,7 @@ Also try using [TypeWiz](https://github.com/urish/typewiz) to add types. More resources +- [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) - [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` - [Migrating an EJECTED CRA app to TS](https://spin.atomicobject.com/2018/07/04/migrating-cra-typescript/) - [Lyft's JS to TS migration tool](https://github.com/lyft/react-javascript-to-typescript-transform) (includes PropTypes migration) @@ -151,6 +152,7 @@ Old content that is possibly out of date ## Misc writeups by notable companies +- [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) - [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 4ecf70c8150fe853a2ed7ce53b4aca85dc319944 Mon Sep 17 00:00:00 2001 From: akameco Date: Fri, 21 Jun 2019 15:19:40 +0900 Subject: [PATCH 116/791] docs(ADVANCED.md): add syntax highlight --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 5b05f34e..21673119 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -681,7 +681,7 @@ More info on places you can use [const assertions](https://blog.logrocket.com/co 2. Higher order type inference from generic constructors -``` +```tsx type ComponentClass

= new (props: P) => Component

; declare class Component

{ props: P; From 1e1541c5703d33bffc000e7ad9985b9099b513fc Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 21 Jun 2019 10:36:50 +0000 Subject: [PATCH 117/791] Prettify ADVANCED.md --- ADVANCED.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 21673119..5025a256 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -684,20 +684,18 @@ More info on places you can use [const assertions](https://blog.logrocket.com/co ```tsx type ComponentClass

= new (props: P) => Component

; declare class Component

{ - props: P; - constructor(props: P); + props: P; + constructor(props: P); } declare function myHoc

(C: ComponentClass

): ComponentClass

; -type NestedProps = { foo: number, stuff: T }; +type NestedProps = { foo: number; stuff: T }; -declare class GenericComponent extends Component> { -} +declare class GenericComponent extends Component> {} // type is 'new (props: NestedProps) => Component>' const GenericComponent2 = myHoc(GenericComponent); - ``` ## TypeScript Roadmap From 2d936e9f56fd897417effe2ed502e5ff757c4009 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 21 Jun 2019 10:48:46 -0400 Subject: [PATCH 118/791] add andrew's post --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 5025a256..43167c8c 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -321,6 +321,8 @@ const LinkButton: React.FunctionComponent = (props) => ( +You may also want to use Discriminated Unions, this section is yet unwritten but please check out [Expressive React Component APIs with Discriminated Unions](https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/). + ## Props: One or the Other but not Both Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both: From 260a2d0c7eb1f018534098becee1c971959dc0d9 Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 22 Jun 2019 09:45:38 -0400 Subject: [PATCH 119/791] add information from Programming Typescript book add information from Programming Typescript book --- README.md | 49 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7cf61892..9e88c7dc 100644 --- a/README.md +++ b/README.md @@ -628,7 +628,7 @@ type AppProps = { status: 'waiting' | 'success'; /** any object as long as you dont use its properties (not common) */ obj: object; - obj2: {}; // same + obj2: {}; // almost the same as `object`, exactly the same as `Object` /** an object with defined properties (preferred) */ obj3: { id: string; @@ -681,16 +681,10 @@ Quote [@ferdaber](https://github.com/sw-yx/react-typescript-cheatsheet/issues/57 ## Forms and Events -If performance is not an issue, inlining handlers is easiest as you can just use type inference: +If performance is not an issue, 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): ```tsx -const el = ( - + {JSON.stringify(state, null, 2)} +

+ ); +} +``` + +You can then use them and get nice type safety through type inference: + +```tsx +ReactDOM.render( + ( +
  • + {item.toPrecision(3)} // Error: Property 'toPrecision' does not exist on type 'string'. +
  • + )} + />, + document.body, +) +``` + +As of [TS 2.9](#typescript-29), you can also supply the type parameter in your JSX to opt out of type inference: + +```tsx +ReactDOM.render( + + items={['a','b']} // Error: Type 'string' is not assignable to type 'number'. + renderItem={item => ( +
  • + {item.toPrecision(3)} +
  • + )} + />, + document.body, +) +``` + +You can do this for [class components](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71) too (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)) + + ## Typing a Component that Accepts Different Props Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". From f95024681d3a88ae6255cc893c3b0c60bcc0699d Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 19 Aug 2019 01:46:48 +0000 Subject: [PATCH 154/791] Prettify ADVANCED.md --- ADVANCED.md | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 2d971a17..5aba5aa7 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -143,7 +143,7 @@ interface Props { } function List(props: Props) { const { items, renderItem } = props; - const [state, setState] = React.useState([]) + const [state, setState] = React.useState([]); return (
    {items.map(renderItem)} @@ -159,15 +159,16 @@ You can then use them and get nice type safety through type inference: ```tsx ReactDOM.render( (
  • - {item.toPrecision(3)} // Error: Property 'toPrecision' does not exist on type 'string'. + {item.toPrecision(3)} // Error: Property 'toPrecision' does not exist on + type 'string'.
  • )} />, - document.body, -) + document.body +); ``` As of [TS 2.9](#typescript-29), you can also supply the type parameter in your JSX to opt out of type inference: @@ -175,20 +176,15 @@ As of [TS 2.9](#typescript-29), you can also supply the type parameter in your J ```tsx ReactDOM.render( - items={['a','b']} // Error: Type 'string' is not assignable to type 'number'. - renderItem={item => ( -
  • - {item.toPrecision(3)} -
  • - )} + items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'. + renderItem={item =>
  • {item.toPrecision(3)}
  • } />, - document.body, -) + document.body +); ``` You can do this for [class components](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71) too (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)) - ## Typing a Component that Accepts Different Props Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". From e1f45ebaa33f280a185e2db3a45f846fa5f025b3 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 20 Aug 2019 09:16:31 -0400 Subject: [PATCH 155/791] add priceline to MIGRATING --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index ae984f72..b650d67b 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -220,6 +220,7 @@ Old content that is possibly out of date - [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) - [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) +- [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) ## Links From a7981361d96b6feea50353176156157fff75d73d Mon Sep 17 00:00:00 2001 From: Alex Bolenok Date: Thu, 22 Aug 2019 22:16:01 +0300 Subject: [PATCH 156/791] More on generic functional components --- ADVANCED.md | 94 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 5aba5aa7..96b71706 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -154,7 +154,30 @@ function List(props: Props) { } ``` -You can then use them and get nice type safety through type inference: +The same using fat arrow function style: + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} + +const List = (props: Props) => { + const { items, renderItem } = props; + const [state, setState] = React.useState([]); + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(state, null, 2)} +
    + ); +} +``` + +Note the `` before the function definition. You can't use just `` as it will confuse the TSX parser. + +You can then use the generic components and get nice type safety through type inference: ```tsx ReactDOM.render( @@ -185,6 +208,75 @@ ReactDOM.render( You can do this for [class components](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71) too (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)) +### Generic components with children + +`children` is usually not defined as a part of the props type. Unless `children` are explicitly defined as a part of the `props` type, an attempt to use `props.children` in JSX or in the function body will fail: + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; +} + +/* Property 'children' does not exist on type 'WrapperProps'. */ +const Wrapper = (props: WrapperProps) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; + +/* +Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps'. + Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps'. +*/ + +const wrapper = ( + item}> + {test} + +); +``` + +To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; + children: string; // The component will only accept a single string child +} + +const Wrapper = (props: WrapperProps) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; +``` + +or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does): + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; +} + +const Wrapper = (props: React.PropsWithChildren>) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; +``` + ## Typing a Component that Accepts Different Props Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". From 8278636c33462c4c5c8d1f3c4581953266e14649 Mon Sep 17 00:00:00 2001 From: Tim Perry Date: Fri, 23 Aug 2019 19:13:31 +0200 Subject: [PATCH 157/791] Link 'basic cheetsheet' directly to the basic TOC (#136) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 64db6be4..7c944337 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ ## All React + TypeScript Cheatsheets -- **The Basic Cheatsheet** ([`/README.md`](/README.md)) is focused on helping React devs just start using TS in React **apps** +- **The Basic Cheatsheet** ([`/README.md`](/README.md#basic-cheatsheet-table-of-contents)) is focused on helping React devs just start using TS in React **apps** - focus on opinionated best practices, copy+pastable examples - explains some basic TS types usage and setup along the way - answers the most Frequently Asked Questions From dd3bedb1d754d608700a12c0f5f5a23c0b78d0b2 Mon Sep 17 00:00:00 2001 From: Alex Bolenok Date: Sat, 24 Aug 2019 01:24:51 +0300 Subject: [PATCH 158/791] Update ADVANCED.md Co-Authored-By: Ferdy Budhidharma --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 96b71706..a73249cc 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -162,7 +162,7 @@ interface Props { renderItem: (item: T) => React.ReactNode; } -const List = (props: Props) => { +const List = (props: Props) => { const { items, renderItem } = props; const [state, setState] = React.useState([]); return ( From d7b77de676e1d7d99c47e1f67da726fc16fa6ab0 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 23 Aug 2019 19:38:36 -0400 Subject: [PATCH 159/791] add pointer to @saltycrane cheatsheet add pointer to @saltycrane cheatsheet --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 5aba5aa7..53c88b60 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1123,6 +1123,8 @@ Any other tips? Please contribute on this topic! [We have an ongoing issue here The `@types` typings export both "public" types meant for your use as well as "private" types that are for internal use. +Check [SaltyCrane's React TypeScript Cheatsheet](https://github.com/saltycrane/typescript-cheatsheet) for a nice autogenerated complete reference. + ## `@types/react` [Link to `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts) From 5580e960701797b7622e07d3b95ea64ae0139c63 Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Mon, 26 Aug 2019 02:16:01 +0900 Subject: [PATCH 160/791] README: Add Example App section & a example link --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 7c944337..00fa739a 100644 --- a/README.md +++ b/README.md @@ -1547,6 +1547,9 @@ It is worth mentioning some resources to help you get started: - Basarat's Deep Dive: https://basarat.gitbooks.io/typescript/ - 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`) +# Example App +- [React TypeScript Todo Example 2019 Mid](https://github.com/ryota-murakami/react-typescript-todo-example-2019) + # My question isn't answered here! - Check out [the Advanced Cheatsheet](/ADVANCED.md) From 5388db69d7669c0ebc344c25451fe99c36b890cb Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 02:40:37 +0000 Subject: [PATCH 161/791] Prettify ADVANCED.md --- ADVANCED.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index fc465bfb..f24a899f 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -172,7 +172,7 @@ const List = (props: Props) => { {JSON.stringify(state, null, 2)}
    ); -} +}; ``` Note the `` before the function definition. You can't use just `` as it will confuse the TSX parser. @@ -267,7 +267,9 @@ interface WrapperProps { renderItem: (item: T) => React.ReactNode; } -const Wrapper = (props: React.PropsWithChildren>) => { +const Wrapper = ( + props: React.PropsWithChildren> +) => { return (
    {props.renderItem(props.item)} From d8346b188e1690bf74db81469bddffc7e1cccbbd Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 05:07:47 +0000 Subject: [PATCH 162/791] Prettify README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 00fa739a..ae20ebde 100644 --- a/README.md +++ b/README.md @@ -1548,6 +1548,7 @@ It is worth mentioning some resources to help you get started: - 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`) # Example App + - [React TypeScript Todo Example 2019 Mid](https://github.com/ryota-murakami/react-typescript-todo-example-2019) # My question isn't answered here! From 6ded64b774ea209adc956b9c2656b0d28ed11e1b Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 26 Aug 2019 01:24:18 -0400 Subject: [PATCH 163/791] `as const` life for context examples --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ae20ebde..b60f5653 100644 --- a/README.md +++ b/README.md @@ -784,7 +784,7 @@ Of course, if you're making any sort of significant form, [you should use Formik ## Context -Using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with no `defaultValue`, yet no need to check for `undefined`: +Using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with **no `defaultValue`, yet no need to check for `undefined`**: ```tsx // create context with no upfront defaultValue @@ -796,12 +796,12 @@ function createCtx() { if (!c) throw new Error("useCtx must be inside a Provider with a value"); return c; } - return [useCtx, ctx.Provider] as [() => A, typeof ctx.Provider]; + return [useCtx, ctx.Provider] as const // make TypeScript infer a tuple, not an array of union types } // usage -export const [useCtx, SettingProvider] = createCtx(); // no need to specify value upfront! +export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! export function App() { const key = useCustomHook("key"); // get a value from a hook, must be in a component return ( @@ -830,7 +830,7 @@ export function createCtx(defaultValue: A) { const [state, update] = React.useState(defaultValue); return ; } - return [ctx, Provider] as [typeof ctx, typeof Provider]; + return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] } // usage From 8badf781b15d2c95e646bb54383d40c252047d7b Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2019 05:24:21 +0000 Subject: [PATCH 164/791] Prettify README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b60f5653..0f33bafa 100644 --- a/README.md +++ b/README.md @@ -796,7 +796,7 @@ function createCtx() { if (!c) throw new Error("useCtx must be inside a Provider with a value"); return c; } - return [useCtx, ctx.Provider] as const // make TypeScript infer a tuple, not an array of union types + return [useCtx, ctx.Provider] as const; // make TypeScript infer a tuple, not an array of union types } // usage From e9fdb4d0a4fbf33447226c9171cfa54bf4a8b9a0 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 3 Sep 2019 11:05:50 -0400 Subject: [PATCH 165/791] add unique keyword note for type branding --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 0f33bafa..f1353ebf 100644 --- a/README.md +++ b/README.md @@ -1154,6 +1154,8 @@ function queryForUser(id: UserID) { queryForUser(OrderID("foobar")); // Error, Argument of type 'OrderID' is not assignable to parameter of type 'UserID' ``` +In future you can use the `unique` keyword to brand. [See this PR](https://github.com/microsoft/TypeScript/pull/33038). + ## Intersection Types 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`: From 9afe62359170e9002110e6efc1ed8f34dd4fb5e2 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 4 Sep 2019 22:27:59 -0400 Subject: [PATCH 166/791] add notes on conditional types --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index f1353ebf..8543ddf8 100644 --- a/README.md +++ b/README.md @@ -1335,6 +1335,11 @@ Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 a - `as`: type assertion - `is`: type guard for function return types +Conditional Types are a difficult topic to get around so here are some extra resources: + +- fully walked through explanation https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/ +- Bailing out and other advanced topics https://github.com/sw-yx/ts-spec/blob/master/conditional-types.md + # 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): From 7c03af5fc8ce996edffacd2658340c0c0376c39a Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 9 Sep 2019 00:37:14 -0400 Subject: [PATCH 167/791] add react loop --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index b650d67b..2ea0b749 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -221,6 +221,7 @@ Old content that is possibly out of date - [Tiny][tiny] - [Talk from ForwardJS here](https://www.slideshare.net/tiny/porting-100k-lines-of-code-to-typescript) - [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) +- Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) ## Links From 33df711d5d399873e41eabf0e0bd7cfc189cdc82 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 9 Sep 2019 00:52:46 -0400 Subject: [PATCH 168/791] Update HOC.md --- HOC.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/HOC.md b/HOC.md index f353ac02..576c9236 100644 --- a/HOC.md +++ b/HOC.md @@ -509,9 +509,10 @@ function withOwner(owner: string) { _Note: above is an incomplete, nonworking example. PR a fix!_ -## Good articles +## Learn More We will need to extract lessons from here in future but here they are: - https://medium.com/@xfor/typescript-react-hocs-context-api-cb46da611f12 - https://medium.com/@jrwebdev/react-higher-order-component-patterns-in-typescript-42278f7590fb +- https://www.matthewgerstman.com/tech/ts-tricks-higher-order-components/ From 087fe838366b27f2463d8958b00e20cc24271a79 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 9 Sep 2019 04:52:48 +0000 Subject: [PATCH 169/791] Prettify HOC.md --- HOC.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/HOC.md b/HOC.md index 576c9236..f4f8cc69 100644 --- a/HOC.md +++ b/HOC.md @@ -141,7 +141,6 @@ export function inject( 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-typescript-cheatsheet/blob/master/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. - # Section 1: React HOC docs in TypeScript In this first section we refer closely to [the React docs on HOCs](https://reactjs.org/docs/higher-order-components.html) and offer direct TypeScript parallels. @@ -269,7 +268,9 @@ export function withSubscription, C>( render() { // the typing for spreading this.props is... very complex. best way right now is to just type it as any // data will still be typechecked - return ; + return ( + + ); } }; // return WithData; @@ -303,7 +304,7 @@ function logProps(WrappedComponent: React.ComponentType) { } render() { // Wraps the input component in a container, without mutating it. Good! - return ; + return ; } }; } From dd4eaa33ebfbc3fb1bd942f84cf95191a5e3e6f8 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 10 Sep 2019 04:45:41 -0400 Subject: [PATCH 170/791] add Google TS 3.5 notes --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index f24a899f..d5f76620 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -971,6 +971,8 @@ declare class GenericComponent extends Component> {} const GenericComponent2 = myHoc(GenericComponent); ``` +See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272) + ## TypeScript Roadmap https://github.com/Microsoft/TypeScript/wiki/Roadmap From ba3519d5f5c5360b220383de11ec4dc6273243d5 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 10 Sep 2019 10:08:10 -0400 Subject: [PATCH 171/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 2ea0b749..1b367be0 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -205,6 +205,7 @@ Old content that is possibly out of date - [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) - [MemSQL's Studio's migration](https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/) - blogpost with many useful tips +- [React Native CLI](https://github.com/react-native-community/cli/issues/683) ## Results From e406a041caa961463ccc4e5afe80b884867fa16d Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Wed, 11 Sep 2019 19:54:52 +0900 Subject: [PATCH 172/791] README: add last 2 chapter in the Toc --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8543ddf8..1c714d0f 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,8 @@ - [Editor Tooling and Integration](#editor-tooling-and-integration) - [Other React + TypeScript resources](#other-react--typescript-resources) - [Time to Really Learn TypeScript](#time-to-really-learn-typescript) +- [Example App](#example-app) +- [My question isn't answered here!](#my-question-isnt-answered-here) # Section 1: Setup From 8a50c5d799b9d92c88112f1f72af97f3e39106df Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 11 Sep 2019 17:08:23 -0400 Subject: [PATCH 173/791] add nextjs and redux --- MIGRATING.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/MIGRATING.md b/MIGRATING.md index 1b367be0..0c40cc4a 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -200,12 +200,8 @@ Old content that is possibly out of date - 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) - [Incremental Migration to TypeScript on a Flowtype codebase][entria] at Entria -- [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) -- [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) -- [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) -- [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) - [MemSQL's Studio's migration](https://davidgom.es/porting-30k-lines-of-code-from-flow-to-typescript/) - blogpost with many useful tips -- [React Native CLI](https://github.com/react-native-community/cli/issues/683) + ## Results @@ -214,7 +210,7 @@ Old content that is possibly out of date - Found incorrect function calls for [Tiny][tiny] - Found rarely used, buggy code that was untested for [Tiny][tiny] -## Misc writeups by notable companies +## Misc migration stories by notable companies and open source - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) - [Lyft](https://eng.lyft.com/typescript-at-lyft-64f0702346ea) @@ -224,6 +220,16 @@ Old content that is possibly out of date - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) - Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) +Open Source + +- [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) +- [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) +- [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) +- [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) +- [React Native CLI](https://github.com/react-native-community/cli/issues/683) +- [Next.js](https://nextjs.org/blog/next-9) +- [Redux](https://github.com/reduxjs/redux/pull/3536) + ## Links [hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" From 12e4401cb43687552dfe50c0bdd2f829c115e268 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Wed, 11 Sep 2019 21:08:26 +0000 Subject: [PATCH 174/791] Prettify MIGRATING.md --- MIGRATING.md | 1 - 1 file changed, 1 deletion(-) diff --git a/MIGRATING.md b/MIGRATING.md index 0c40cc4a..9610e062 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -202,7 +202,6 @@ Old content that is possibly out of date - [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 - ## Results - Number of production deploys doubled for [Hootsuite][hootsuite] From 43924fd1e86a35d14ac52a8aeab9a51a6236cf12 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Sun, 15 Sep 2019 15:06:11 +0200 Subject: [PATCH 175/791] chore: Match prettier config currently used for files --- package.json | 3 --- 1 file changed, 3 deletions(-) diff --git a/package.json b/package.json index bd16427b..5016b1ad 100644 --- a/package.json +++ b/package.json @@ -20,8 +20,5 @@ "homepage": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#readme", "devDependencies": { "prettier": "^1.17.0" - }, - "prettier": { - "singleQuote": true } } From bcd0443b2e69e8a2a6ccba95a98aa97db72c1b5e Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Sun, 15 Sep 2019 15:06:21 +0200 Subject: [PATCH 176/791] chore: Format docs --- HOC.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/HOC.md b/HOC.md index f4f8cc69..f3e5583f 100644 --- a/HOC.md +++ b/HOC.md @@ -115,7 +115,7 @@ export function withTheme( const themeProps = getThemePropsFromSomeWhere(); // this.props comes afterwards so the can override the default ones. - return ; + return ; } }; } @@ -132,7 +132,7 @@ export function inject( injector: Pick ) { return function Injected(props: Omit) { - return ; + return ; }; } ``` @@ -268,9 +268,7 @@ export function withSubscription, C>( render() { // the typing for spreading this.props is... very complex. best way right now is to just type it as any // data will still be typechecked - return ( - - ); + return ; } }; // return WithData; @@ -304,7 +302,7 @@ function logProps(WrappedComponent: React.ComponentType) { } render() { // Wraps the input component in a container, without mutating it. Good! - return ; + return ; } }; } From 0136afd62df2b2194f2bda59c9366ab3b4febf20 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 16 Sep 2019 02:42:42 +0000 Subject: [PATCH 177/791] Prettify HOC.md --- HOC.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/HOC.md b/HOC.md index f3e5583f..f4f8cc69 100644 --- a/HOC.md +++ b/HOC.md @@ -115,7 +115,7 @@ export function withTheme( const themeProps = getThemePropsFromSomeWhere(); // this.props comes afterwards so the can override the default ones. - return ; + return ; } }; } @@ -132,7 +132,7 @@ export function inject( injector: Pick ) { return function Injected(props: Omit) { - return ; + return ; }; } ``` @@ -268,7 +268,9 @@ export function withSubscription, C>( render() { // the typing for spreading this.props is... very complex. best way right now is to just type it as any // data will still be typechecked - return ; + return ( + + ); } }; // return WithData; @@ -302,7 +304,7 @@ function logProps(WrappedComponent: React.ComponentType) { } render() { // Wraps the input component in a container, without mutating it. Good! - return ; + return ; } }; } From 0d88dfd964c84327d7e5840f5e0dc8db6dbfea15 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Tue, 17 Sep 2019 02:27:20 +0200 Subject: [PATCH 178/791] docs: Add example for img[loading] (#143) --- ADVANCED.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index d5f76620..f0e1d1b7 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -77,6 +77,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Linting](#linting) - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) + - [Adding non-standard attributes](#adding-non-standard-attributes) # Section 0: Utility Types @@ -1276,6 +1277,28 @@ Anything not listed above is considered an internal type and not public. If you' - `StatelessComponent` - `ReactType` +### 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 +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 +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`. + +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: + +```ts +// react-unstable-attributes.d.ts +import "react"; + +declare module "react" { + interface ImgHTMLAttributes extends HTMLAttributes { + loading?: "auto" | "eager" | "lazy"; + } +} +``` + ## `@types/react-dom` To be written From 12fe191902fdfe69860b94b8e263f95389ed07de Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Tue, 17 Sep 2019 00:27:28 +0000 Subject: [PATCH 179/791] Prettify ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index f0e1d1b7..b7429881 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -78,7 +78,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) - [Adding non-standard attributes](#adding-non-standard-attributes) - + # Section 0: Utility Types From e85986b1c49c85f7a14728cbffd8b478f1c52683 Mon Sep 17 00:00:00 2001 From: swyx Date: Sun, 22 Sep 2019 19:27:57 -0400 Subject: [PATCH 180/791] fix conditioanl props example --- ADVANCED.md | 56 ++++++++++++++++------------------------------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index b7429881..ae099365 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -492,49 +492,27 @@ You want to allow `expanded` to be passed only if `truncate` is also passed, bec You can do this by function overloads: ```tsx -import React from "react"; - type CommonProps = { children: React.ReactNode; - as: "p" | "span" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6"; -}; - -type NoTruncateProps = CommonProps & { - truncate?: false; + miscProps?: any; }; -type TruncateProps = CommonProps & { - truncate: true; - expanded?: boolean; -}; +type NoTruncateProps = CommonProps & { truncate?: false }; -// Type guard -const isTruncateProps = ( - props: NoTruncateProps | TruncateProps -): props is TruncateProps => !!props.truncate; +type TruncateProps = CommonProps & { truncate: true; expanded?: boolean }; // Function overloads to accept both prop types NoTruncateProps & TruncateProps -function Text(props: NoTruncateProps | TruncateProps) { - if (isTruncateProps(props)) { - const { children, as: Tag, truncate, expanded, ...otherProps } = props; - - const classNames = truncate ? ".truncate" : ""; - - return ( - - {children} - - ); - } - - const { children, as: Tag, ...otherProps } = props; - - return {children}; +function Text(props: NoTruncateProps): JSX.Element +function Text(props: TruncateProps): JSX.Element +function Text(props: CommonProps & {truncate?: boolean; expanded?: boolean}) { + const { children, truncate, expanded, ...otherProps } = props; + const classNames = truncate ? ".truncate" : ""; + return ( +
    + {children} +
    + ) } - -Text.defaultProps = { - as: "span" -}; ``` Using the Text component: @@ -542,13 +520,13 @@ Using the Text component: ```tsx const App: React.FC = () => ( <> - not truncated {/* works */} - truncated {/* works */} + {/* these all typecheck */} + not truncated + truncated truncate-able but expanded - {/* works */} - {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type 'Pick'} */} + {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */} truncate-able but expanded ); From e7bc2e19d80aedd62aabbfde1472b781f7ff1bbe Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Sun, 22 Sep 2019 23:28:02 +0000 Subject: [PATCH 181/791] Prettify ADVANCED.md --- ADVANCED.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index ae099365..cfaa6440 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -502,16 +502,16 @@ type NoTruncateProps = CommonProps & { truncate?: false }; type TruncateProps = CommonProps & { truncate: true; expanded?: boolean }; // Function overloads to accept both prop types NoTruncateProps & TruncateProps -function Text(props: NoTruncateProps): JSX.Element -function Text(props: TruncateProps): JSX.Element -function Text(props: CommonProps & {truncate?: boolean; expanded?: boolean}) { +function Text(props: NoTruncateProps): JSX.Element; +function Text(props: TruncateProps): JSX.Element; +function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) { const { children, truncate, expanded, ...otherProps } = props; const classNames = truncate ? ".truncate" : ""; return (
    {children}
    - ) + ); } ``` From 451aa8aae0c02a0e39b91508c3b7825d744c729f Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 24 Sep 2019 01:43:34 -0400 Subject: [PATCH 182/791] Update MIGRATING.md --- MIGRATING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING.md b/MIGRATING.md index 9610e062..2cf710cf 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -215,7 +215,7 @@ Old content that is possibly out of date - [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) -- [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) +- [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) ([podcast](https://softwareengineeringdaily.com/2017/08/11/typescript-at-slack-with-felix-rieseberg/)) - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) - Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) From 497b72441e948f0cef043a5d82afb7e12a36c6d2 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 24 Sep 2019 12:57:29 -0400 Subject: [PATCH 183/791] add conditionally rendering components example --- ADVANCED.md | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index cfaa6440..6c88e7fe 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -51,6 +51,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Section 1: Reusable Components/Type Utilities](#section-1-reusable-componentstype-utilities) - [Higher Order Components](#higher-order-components-hocs) - [Render Props](#render-props) + - [Conditionally Rendering Components](#conditinonally-rendering-components) - [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered) - [Typing a Component that Accepts Different Props](#typing-a-component-that-accepts-different-props) - [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both) @@ -119,6 +120,55 @@ export interface Props { [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose). +## Conditionally Rendering Components + +Use [type guards](https://basarat.gitbooks.io/typescript/docs/types/typeGuard.html#user-defined-type-guards)! + +```tsx +// Button props +type ButtonProps = React.ButtonHTMLAttributes & { + href?: undefined +} + +// Anchor props +type AnchorProps = React.AnchorHTMLAttributes & { + href?: string +} + +// Input/output options +type Overload = { + (props: ButtonProps): JSX.Element + (props: AnchorProps): JSX.Element +} + +// Guard to check if href exists in props +const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps => 'href' in props + +// Component +const Button: Overload = (props: ButtonProps | AnchorProps) => { + // anchor render + if (hasHref(props)) return
    + // button render + return + {/* 😭 Error, `disabled` doesnt exist on anchor element */} + + + ) +} +``` + ## `as` props (passing a component to be rendered) `ElementType` is pretty useful to cover most types that can be passed to createElement e.g. From 7ac3a293b97730d92914f498308574b36e1c11eb Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Tue, 24 Sep 2019 17:38:29 +0000 Subject: [PATCH 184/791] Prettify ADVANCED.md --- ADVANCED.md | 45 +++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 6c88e7fe..b3ccc852 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -127,45 +127,46 @@ Use [type guards](https://basarat.gitbooks.io/typescript/docs/types/typeGuard.ht ```tsx // Button props type ButtonProps = React.ButtonHTMLAttributes & { - href?: undefined -} + href?: undefined; +}; // Anchor props type AnchorProps = React.AnchorHTMLAttributes & { - href?: string -} + href?: string; +}; // Input/output options type Overload = { - (props: ButtonProps): JSX.Element - (props: AnchorProps): JSX.Element -} + (props: ButtonProps): JSX.Element; + (props: AnchorProps): JSX.Element; +}; // Guard to check if href exists in props -const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps => 'href' in props +const hasHref = (props: ButtonProps | AnchorProps): props is AnchorProps => + "href" in props; // Component const Button: Overload = (props: ButtonProps | AnchorProps) => { // anchor render - if (hasHref(props)) return + if (hasHref(props)) return ; // button render - return - {/* 😭 Error, `disabled` doesnt exist on anchor element */} - - - ) + <> + {/* 😎 All good */} + + {/* 😭 Error, `disabled` doesnt exist on anchor element */} + + + ); } ``` From 815ef416db7c3c143ddfd4dcbc602ea5672937c9 Mon Sep 17 00:00:00 2001 From: Veniamin Krol <153412+vkrol@users.noreply.github.com> Date: Sat, 28 Sep 2019 19:47:19 +0300 Subject: [PATCH 185/791] Replace the term "constructor" with the term "parameter list" --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c714d0f..dff51b7d 100644 --- a/README.md +++ b/README.md @@ -183,7 +183,7 @@ const Title: React.FunctionComponent<{ title: string }> = ({ }) =>
    {children}
    ; ``` -- _In the future_, it may automatically mark props as `readonly`, though that's a moot point if the props object is destructured in the constructor. +- _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. - `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). From ffa2c55f57f4dac8738dd72146041eae4ff09f85 Mon Sep 17 00:00:00 2001 From: andreasroth Date: Thu, 3 Oct 2019 10:42:01 +0200 Subject: [PATCH 186/791] Add tipp about Props & Omit to advanced.md --- ADVANCED.md | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 495cf03f..702afa4b 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -476,6 +476,21 @@ export const Checkbox = ( }; ``` +When your component defines multiple props, chances of those conflicts increase. However you can explicitly state that all your fields should be removed from the underlying component using the `keyof` operator: + +```tsx +export interface Props { + label: React.ReactNode; // conflicts with the InputElement's label + onChange: (text: string) => void; // conflicts with InputElement's onChange +} + +export const Textbox = ( + props: Props & Omit, keyof Props> +) => { + // implement Textbox component ... +} +``` + ## Type Zoo As you can see from the Omit example above, you can write significant logic in your types as well. [type-zoo](https://github.com/pelotom/type-zoo) is a nice toolkit of operators you may wish to check out (includes Omit), as well as [utility-types](https://github.com/piotrwitek/utility-types) (especially for those migrating from Flow). From 12bc96929562ed8ccaa162bc4f8f3cbffbf89721 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Thu, 3 Oct 2019 23:49:30 +0530 Subject: [PATCH 187/791] Basic getDerivedStateFromProps --- README.md | 63 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/README.md b/README.md index dff51b7d..964292e8 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,7 @@ - [Types or Interfaces?](#types-or-interfaces) - [Basic Prop Types Examples](#basic-prop-types-examples) - [Useful React Prop Type Examples](#useful-react-prop-type-examples) + - [getDerivedStateFromProps](#getDerivedStateFromProps) - [Forms and Events](#forms-and-events) - [Context](#context) - [forwardRef/createRef](#forwardrefcreateref) @@ -687,6 +688,68 @@ Quote [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-che [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). +## getDerivedStateFromProps + +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 easily achieved using hooks which can also help set up memoization easily. + +Here are a few ways in which you can annotate `getDerivedStateFromProps` + +1. If you have explicitly typed your derived state and want to make sure that the return value from `getDerivedStateFromProps` conforms to it. + +```tsx +class Comp extends React.Component { + static getDerivedStateFromProps( + props: Props, + state: State + ): Partial | null { + // + } +} +``` + +2. When you want the function's return value to determine your state. + +```tsx +class Comp extends React.Component< + Props, + ReturnType +> { + static getDerivedStateFromProps(props: Props) {} +} +``` + +3. When you want derived state with other state fields and memoization + +```tsx +type CustomValue = any; +interface Props { + propA: CustomValue; +} +interface DefinedState { + otherStateField: string; +} +type State = DefinedState & ReturnType; +function transformPropsToState(props: Props) { + return { + savedPropA: props.propA, // save for memoization + derivedState: props.propA + }; +} +class Comp extends React.PureComponent { + constructor(props: Props) { + super(props); + this.state = { + otherStateField: "123", + ...transformPropsToState(props) + }; + } + static getDerivedStateFromProps(props: Props, state: State) { + if (isEqual(props.propA, state.savedPropA)) return null; + return transformPropsToState(props); + } +} +``` + ## Forms and Events If performance is not an issue, 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): From 7b888305fbf523dffa4ced8d138e2eaac11b71f2 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 8 Oct 2019 16:04:04 -0400 Subject: [PATCH 188/791] add note about omit being official --- ADVANCED.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 702afa4b..6aa00c4e 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -451,6 +451,8 @@ Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) ## Omit attribute from a type +Note: [Omit was added as a first class utility in TS 3.5](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk)! 🎉 + Sometimes when intersecting types, we want to define our own version of an attribute. For example, I want my component to have a `label`, but the type I am intersecting with also has a `label` attribute. Here's how to extract that out: ```tsx @@ -458,6 +460,7 @@ export interface Props { label: React.ReactNode; // this will conflict with the InputElement's label } +// note: Omit was added as a first class utility in TS 3.5 type Omit = Pick>; // usage From 1969966d1da0e4239019bad203bfea0c3496a7f7 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:04:58 +0000 Subject: [PATCH 189/791] Prettify ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index ee6929ff..6067dd46 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -625,7 +625,7 @@ export const Textbox = ( props: Props & Omit, keyof Props> ) => { // implement Textbox component ... -} +}; ``` ## Type Zoo From 8ec722ecec9f9c57bb3f3bfca1da36d042bafa3f Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:06:08 +0000 Subject: [PATCH 190/791] docs: update CONTRIBUTORS.md --- CONTRIBUTORS.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 46670c0f..feb954fe 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -2,7 +2,16 @@ -
    Ferdy Budhidharma
    Ferdy Budhidharma

    👀 🚧 🖋
    swyx
    swyx

    🤔 👀 🚧 🖋 💬
    Sebastian Silbermann
    Sebastian Silbermann

    👀 🚧 🖋
    Islam Attrash
    Islam Attrash

    🚧 🖋
    Stephen Koo
    Stephen Koo

    💬 💡
    + + + + + + + + + +
    Ferdy Budhidharma
    Ferdy Budhidharma

    👀 🚧 🖋
    swyx
    swyx

    🤔 👀 🚧 🖋 💬
    Sebastian Silbermann
    Sebastian Silbermann

    👀 🚧 🖋
    Islam Attrash
    Islam Attrash

    🚧 🖋
    Stephen Koo
    Stephen Koo

    💬 💡
    Andreas
    Andreas

    💻 📖 🚇
    From 187e316529d851c5ef12c2ac9618a597332bbd72 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:06:09 +0000 Subject: [PATCH 191/791] docs: update README.md From f87c7df1df5551d1208e9b7104d5126f8e3b78bc Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2019 20:06:10 +0000 Subject: [PATCH 192/791] docs: update .all-contributorsrc --- .all-contributorsrc | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index c0a1b7e4..c9b6269c 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -61,6 +61,17 @@ "question", "example" ] + }, + { + "login": "andreasgruenh", + "name": "Andreas", + "avatar_url": "https://avatars2.githubusercontent.com/u/12122390?v=4", + "profile": "https://github.com/andreasgruenh", + "contributions": [ + "code", + "doc", + "infra" + ] } ], "contributorsPerLine": 7, From 09d20c2e940f7fc3691d80bc895184b49847e595 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Oct 2019 15:34:10 -0700 Subject: [PATCH 193/791] feat: add ts examples to README --- README.md | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 964292e8..3eeb4ceb 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ function TextInputWithFocusButton() { ); } ``` +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2&ssl=15&ssc=1&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) @@ -331,6 +332,7 @@ export function reducer(state: AppState, action: Action): AppState { } } ``` +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) **Custom Hooks** @@ -346,12 +348,13 @@ export function useLoading() { return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) This way, when you destructure you actually get the right types based on destructure position.
    Alternative: Asserting a tuple return type - + If you are [having trouble with const assertions](https://github.com/babel/babel/issues/9800), you can also assert or define the function return types: ```tsx @@ -433,6 +436,7 @@ class App extends React.Component { } } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) Don't forget that you can export/import/extend these types/interfaces for reuse. @@ -485,6 +489,7 @@ class App extends React.Component<{ message: string }, { count: number }> { }; } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=22&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) **Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: @@ -505,6 +510,7 @@ class App extends React.Component<{ } } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=17&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). @@ -749,6 +755,7 @@ class Comp extends React.PureComponent { } } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=2&ssc=28&pln=2&pc=22#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) ## Forms and Events @@ -791,6 +798,7 @@ class App extends React.Component< } } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) Instead of typing the arguments and return values with `React.FormEvent<>` and `void`, you may alternatively apply types to the event handler itself (_contributed by @TomasHubelbauer_): @@ -844,6 +852,7 @@ If you don't quite care about the type of the event, you can just use React.Synt
    ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&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), which is written in TypeScript. @@ -880,6 +889,7 @@ export function Component() { return
    {key}
    ; } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: @@ -910,7 +920,7 @@ export function App() { ); } export function Component() { - const { state, update } = React.useContext(TextProvider); + const { state, update } = React.useContext(TextContext); return (
    ); ``` +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) @@ -710,6 +713,7 @@ try { } } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing: @@ -1069,6 +1073,7 @@ export default function MyComponent({ label = "foobar" }: MyProps) { return
    Hello world {label}
    ; } ``` +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/README.md b/README.md index 3eeb4ceb..b6ec5567 100644 --- a/README.md +++ b/README.md @@ -348,7 +348,7 @@ export function useLoading() { return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) This way, when you destructure you actually get the right types based on destructure position. @@ -436,7 +436,7 @@ class App extends React.Component { } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) Don't forget that you can export/import/extend these types/interfaces for reuse. @@ -489,7 +489,7 @@ class App extends React.Component<{ message: string }, { count: number }> { }; } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=22&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=22&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) **Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: @@ -510,7 +510,7 @@ class App extends React.Component<{ } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=17&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=17&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). @@ -755,7 +755,7 @@ class Comp extends React.PureComponent { } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=2&ssc=28&pln=2&pc=22#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=2&ssc=28&pln=2&pc=22#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) ## Forms and Events @@ -798,7 +798,7 @@ class App extends React.Component< } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) Instead of typing the arguments and return values with `React.FormEvent<>` and `void`, you may alternatively apply types to the event handler itself (_contributed by @TomasHubelbauer_): @@ -852,7 +852,7 @@ If you don't quite care about the type of the event, you can just use React.Synt ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGctoRlM4BeRYmAOgFc6kLABQBKClVoM4AMSbs4o9gD4FFOHAA8mJmrhFMbAN7aozJJgC+u2gGVeAIxDAYRoUgBcndDxsBPGjAAFkgwwGgAogBuSAEiynCGuupI3GBE0QEAIuYovAA2MKIA3Elw1PTwMChQAOYh8ilVtfUodHAwvmBIEKyN1XXwAGQJpckgKMB5noZwkSh5vB5wDFDANDVwFiXk6rtwYK10AO7QACbTs-OLnitrG1ulDzu75VJI45PyTQPc7xN53DmCyQRTgAHowe1Okg0ME0ABrOgAQlKr3gBzoxzOX36IVShxOUFOgKuIPBkI6XVhMMRKOe6ghcBCaG4rN0Fis5CUug0p2AkW59M0eRQ9iQeUFe3U4Q+U1GmjWYF4lWhbAARH9Jmq4DQUCAkOrNXltWDJbsNGCRWKJTywXyBTz7Wb1BoreLnbsAAoEs7ueUaRXKqFddUYrFE7W6-Whn0R8Eei1um3PC1Ox38hOBlUhtV0BxOGDaoGLdUAGQgGzWJrNqYzFAtJhAgpEQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&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), which is written in TypeScript. @@ -889,7 +889,7 @@ export function Component() { return
    {key}
    ; } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: @@ -929,7 +929,7 @@ export function Component() { ); } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=36&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=36&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. @@ -1041,7 +1041,7 @@ export class Modal extends React.Component { } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=36&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4QIATFAGwQgngF45mIaAK4gkNGADoA5khgBRNklHiAQgE8AkswAUAIias2AWhzddASjgo6cABIAVALIAZBUrEwA3BQD0Pq3R0IsA0UnAhcGoQQlBwABYwIGxwmMCK8dZW-MAAbnAA7sAwceHMBAbsJlww+N6U5EgAHpCwcGhs1jaOLOxwTTBizDbI6JIAwriQNB5wAN4UcH1sAFx2Tq6Kyrz8giIeEmhEKANuW3rMuRZ1C22TtB4AIsDM3ULi2pbz5IuLFRzVEhQYDAgzGcTSOmKwDoEiQbHMdUWAF8KDdqOB7uIAOppNgAVRoTDeMA+cxuvx6-24EiITBySDBEO0UJhcIRNxR9UWRBozCQUFJXx+cCIMBiNEQxBgDwA8o4DkcBgAFaAwdjM8EwsA4MAwtDgtjMHkAGjgLNh8MRcE5SKAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=36&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4QIATFAGwQgngF45mIaAK4gkNGADoA5khgBRNklHiAQgE8AkswAUAIias2AWhzddASjgo6cABIAVALIAZBUrEwA3BQD0Pq3R0IsA0UnAhcGoQQlBwABYwIGxwmMCK8dZW-MAAbnAA7sAwceHMBAbsJlww+N6U5EgAHpCwcGhs1jaOLOxwTTBizDbI6JIAwriQNB5wAN4UcH1sAFx2Tq6Kyrz8giIeEmhEKANuW3rMuRZ1C22TtB4AIsDM3ULi2pbz5IuLFRzVEhQYDAgzGcTSOmKwDoEiQbHMdUWAF8KDdqOB7uIAOppNgAVRoTDeMA+cxuvx6-24EiITBySDBEO0UJhcIRNxR9UWRBozCQUFJXx+cCIMBiNEQxBgDwA8o4DkcBgAFaAwdjM8EwsA4MAwtDgtjMHkAGjgLNh8MRcE5SKAA)
    @@ -1095,7 +1095,7 @@ class App extends React.Component< }; } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) **Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. If `A` and `B` are both object types, `A | B` isn't "either A or B", it is "A or B or both at once", which causes some confusion if you expected it to be the former. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: @@ -1121,7 +1121,7 @@ function isAdmin(user: Admin | User): user is Admin { return (user as any).role !== undefined } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. @@ -1190,7 +1190,7 @@ class MyComponent extends React.Component<{ } } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgGU61gUAbAWSQGduUBzJABVa9ALwFuMKMAB2-fAG4KFOTCRRM6egAUcYbnADeFOHBA8+ggFxwpM+XAA+cAK6yAJkkxykH5eQAvirkaBCyUnAAwriQskiyMABMtsjoMAB0AGJRADx6EAYAfHASABRG5pYCSIEAlKUlZaZwuR7AAG5FLWa5ABYAjEVGFrw1gbkA9IPd5L2T7V0UdSFobCi8cBzUMeDhCfBIAB7qnoZpGBm7cQe5JnNVYzZ20nL8AYEl92ZEnhplDW+ZjgYQi8Eqoys9ECpTgMD6wG4GTA+m4AWBcCIMFcUFkcGaDwxuWu+0SSUeULEI2qgjgG0YzFYnBpwlEn2pT1qUxJ8TJswxdXRcGCQSAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgGU61gUAbAWSQGduUBzJABVa9ALwFuMKMAB2-fAG4KFOTCRRM6egAUcYbnADeFOHBA8+ggFxwpM+XAA+cAK6yAJkkxykH5eQAvirkaBCyUnAAwriQskiyMABMtsjoMAB0AGJRADx6EAYAfHASABRG5pYCSIEAlKUlZaZwuR7AAG5FLWa5ABYAjEVGFrw1gbkA9IPd5L2T7V0UdSFobCi8cBzUMeDhCfBIAB7qnoZpGBm7cQe5JnNVYzZ20nL8AYEl92ZEnhplDW+ZjgYQi8Eqoys9ECpTgMD6wG4GTA+m4AWBcCIMFcUFkcGaDwxuWu+0SSUeULEI2qgjgG0YzFYnBpwlEn2pT1qUxJ8TJswxdXRcGCQSAA) Note that you cannot assert your way to anything - basically it is only for refining types. Therefore it is not the same as "casting" a type. @@ -1266,7 +1266,7 @@ type HumanProps = { export const Human: React.FC = // ... export const Dog: React.FC = // ... ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=20&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgCEUBnJABRzGbgF44BvCnGFoANi2YA5FCCQB+AFxxmMKMAB2AcwA0Q4Suqj5S5OhgA6AMIBlaxwh1YwJMz1x1MpEpVqtcAPT+cACurAAmcBpwAEYQMAAWFAC+VLT0ACIQmvZcvAJ6MCjAosyWEMHqMErqwSDRSFDJqXRwABK1KOo53HyC5MLxnWGl5ZXVtfWN5CnkSAAekLBwaBDqKm0d6ibEFgBilgA8TKzdcABkGyCd3QB8eQAUAJS8d-d6B2HAAG4BNxSPFAo80W8BWa3gmU02zM5n2RxY7E43AukNuD2ePFe70+P38f3IjyAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=20&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgCEUBnJABRzGbgF44BvCnGFoANi2YA5FCCQB+AFxxmMKMAB2AcwA0Q4Suqj5S5OhgA6AMIBlaxwh1YwJMz1x1MpEpVqtcAPT+cACurAAmcBpwAEYQMAAWFAC+VLT0ACIQmvZcvAJ6MCjAosyWEMHqMErqwSDRSFDJqXRwABK1KOo53HyC5MLxnWGl5ZXVtfWN5CnkSAAekLBwaBDqKm0d6ibEFgBilgA8TKzdcABkGyCd3QB8eQAUAJS8d-d6B2HAAG4BNxSPFAo80W8BWa3gmU02zM5n2RxY7E43AukNuD2ePFe70+P38f3IjyAA) Make sure not to confuse Intersection Types (which are **and** operations) with Union Types (which are **or** operations). From b35c7cfee1a0bf22ba8e07f7b11823d06c10d83c Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Oct 2019 15:57:59 -0700 Subject: [PATCH 195/791] feat: add ts playground examples to HOC --- HOC.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HOC.md b/HOC.md index f4f8cc69..d0e1563b 100644 --- a/HOC.md +++ b/HOC.md @@ -217,6 +217,7 @@ function BlogPost({ data, id }: BlogPostProps) { ); } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=15&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCgeirhgAskBnJOFIuxuMHMJuiHABGrYADsAVkgxIAJsICenVgAkA8gGEK4mEiiZ0rAOrAGAERQwUABV5MAPABUAfHADeFOHFmWUALjhHAG44Gm9fOGB+AHMkMT1gNAoAX2paR0j+BlYYBTBWCExwqzS4a0zlbjs4QsqAdygUMHz5NFxIeLF4BksK8Uw9IllSjQrstgwAVxQAG0iuvQM0AqLxhqaWuDbwCE74AApJlnkYQWjGoW8kA0mZmFsIPjhsXEiYAEoKJAAPSFhnyZiDDAXZwOqmegAZUmQiYaCgwDAMBBYicABoynAfroxLJ+CZzL4HnwnM4MRpnPtPKFaAwonQ8qxZjMIHV+EcBPMBlAyhihJN4OcUJdxrl8jUikZGs05Bp2rs4vAWGB2JYkDMlOCGBABW95rp9AkxNEwRDKv09HFlhKytSpRtZfK9gFkOgYAA6ABSkIAGgBRGZIECKuViJgwKCTDDQezWVwAMjgGjR1LCLEDGAsVgqKABQNOPMw0ECqdoPEe-Hprtkuw1wmkKCOohg+H4RBQNbEdfETGAshWlTQMxQTCY1PT0hgWf8cCp5C8Xh8VkhOqgywCYqQtWnK8ma6QKfnC-LfBdqE7Gvs3p97oAMsAhI0oAoALIoMQoWKyACCMAjD4FZh7GTOA1BAUxYwxAAiJcUCg5wEOpd44AAXlcRwKGQjwjzCcYQE-RIKkYIgAmvO8HyfV930-ORf3-fldH4cEZjmKwAGsci4TcbXtFo5R2PY2FxOAiCYCAZgAN2bfh+xuO4qgrUs2GiFAe26LgT34WoCXoacMTqehEnoCoJCOdSCgRaJxFmTFuK1Yz8Fg-ARKDCApPkF48FMNskAAR0mYAiGDLoxyPbjiX4FC4DI+9H3YKiPy-OiEQYoCQLAiDrGg2D4OcIJqW4yErF0VD3GpRdfACYJqWSfKjyIGA9zELZh1HOAdOnLFvhxPFEGID1+I6RVYzsDEirVVxsIXLZdnDSNoygfZNICCKsPKhcmEmfJFs0946umrw6SYd16HfWRAw0U7jVYKKjpOs6Lqu2J3SEcRZH2I69vWw7DOO8M1VKqaDoqqwAgnTNfH2HdV2WDFdu+uBavW1JKCPLxtiGrozD7F8dS6Ur9mQtC4GhvdlndDtZEu99YnvcM4j0D7fvu3FHpppAvtR6aMYVLoTBYgBVMQQDx+AosJ1DnAR0n93dIK3KQanrrpnFGbuq7zsVp6Obq9aNbZ66CaJqW0YXO6WBgcbdH2IHgdgsH1Unacod8Xd9wxO74dNrxkk59aiFxRm1u9mlKjFcQTSLHkmB4c8I84KJ3U0zJ3VTuApOfGbwEDb53XrcMwRQJRLPoeAxFZMZBFMgvuNMNh+HfBQEbCWDTRYuBw2AduRAZfI0EYNAOOGEOGqa2cEa8exeL4p1FWKFAULcc3iqQd1YOSdxU-dJnE+TkchIUd4N6oE3gc56aUZ9-bQ9HqBmo63w6pR6gACoX7gdRRiOGjTQYJNZ5CnAF+VAvi-GgPANoYZ4D8WCjAFWOloSwnhIiZEoIor2UQXCBESIURzi8DAxUKtDxeBdsuGGSAAjTkcIyY2JNXbkPdLEGABCQqE0wrrcgPw-gQNmvAAAQiyaI1gIDhgQTCLBKCUSlQweI5BODdh4LgAIiAQiREwGIbOGW646FWGofkOGdgAgZRgPYZRqjwwRWyr4eCxt1paNXkwsxwjwxLTsO6PsnxyB7SAA)
    @@ -401,6 +402,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { }; } ``` +[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd5qQQkaY64BeOAbQF0A3BSq0GcAMK4WbADLAx3ABQBKLgD44iinDgAeACbAAbnAD0aisuHlq9RlNYwAykgA2SDNC6aA+gC44FBoATwAaOAgAdxoABRwwOgCg4NVODUUAb204YH0AqNj4ugA6XIoAX2UhG1F7ZkcAQQxgUW8VdU0s8h0UfX1JerYAxQYoANHgGgBzVI0maXZisABXOgALTLgYJAAPGHGYKHDcgPnHEvdpmDW4Soqq61sxSRoaD23+hzZvWzeMLW6cDObBc7k8R2ywJgTRgLXolkUAwWcgYD0o5FMpi2ayQdCQgSI2PxYCKWwgcAARvjJgArd5IfSU4JEuAACQA8uIKJNtlBMOh8QB1YDXJzLCl0NBQYBgWG0OIQBK6AAqGi6On0KBgKACyuq5QomGWNGatCBtD+MEUIBQYCc2u2yogCoSAQAYsbTTRwjawAAReRgLVoNZOl2JOAek1ymiqdVwIgwZZQGhwI3RuEq8IxOC7bY0fQcYWi8WS6WyuHhlVqcLiNQAnQ6QVQW1gBkDSBvIaIYgwYod2iOZXBNvV7Jx7I6GAj-Hh7wAKScAA1inIKS2oMEALJBFBTBkNGCHYAU5bbOi6cThdkgEW6GLhABEmu1j7UamqjbMWPERC1kymFlJjeKBzXAQc2GKOBlRxIEUFcNBllcLUGTgOdpzbOAcUJeQWUibD8WufEbSmYA0Cw1tWBKScEyQJMUyBZC6A4AcuxgYtQxxFhcz2VhCx7dA+1Yxx7yKNUaJ0FYKVcMjaILJAoHaeMvx0TFIzokMWRJRUOGCCBljgSIgngWl3igmDcOoJDGSpOB9EHQyRRuWxtj2HI7FQfRigkxsnngX0230e0ULnbhfWCx1nSKRRrnkYoGBQ8JYpKbSEjRFTfNqOAAoZAM6CDGAQ1C7LbTygqQzDaLkvih0kCStY4tSuh0oy79sUa0kmFxQJMF5IyoH4uhySIuDUwgIwFOlfRCNg6b+SQ+BB2owEMsTZNUwbVqdF0ZtKM+cC2J8jKMmKU7qqag0Vq2uATtOnKgtq8NLuuxtbuKe6yuDNYnqOxtzF+lqv2extyk-W59SAA) ## Docs Example: [Wrap the Display Name for Easy Debugging](https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging) From ee797a9b898e70ea9e6635860344d87dfbb50ff1 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2019 22:58:13 +0000 Subject: [PATCH 196/791] Prettify ADVANCED.md --- ADVANCED.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index f2182832..d5ddc631 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -169,6 +169,7 @@ function App() { ); } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=2&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) ## `as` props (passing a component to be rendered) @@ -292,6 +293,7 @@ const wrapper = ( ); ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615AL4UKAegAqOF4ILlgATwI0AAtgABstTXw4LQgkfjhqCHgkAA9gfngIajgYcK4CNg4wkIERUXwHOAC-CjRiwtZ2TnprOBE4PLptDIkvUUMwPn4lKp6oWqExSxtJdSIYAFcoEsN1GUEtYAA3UX2ZSSnQ-gdNHSh9U0nphxMQcx9yC4uJK4EHGLxRJIaifb6CPxHU7qcyeLyefwBCjCcrMfASOCAhKaJSFKA0ADmKlkBjgeMJxLuelJxlJ5OoBJW4npRLgXmSBUy2TgKH4-GABOoKAARnFmDAIKVUQRdLR8dR+WgAIIwGD44WbOgZABkXWqPGmghZDQc6lq9DKUVi2JByVS6S5OXynWKUoq+FlapoipVXo1WrgurmNUNxsaFBavnI7QV8AA7t0wn09l9+sHem8rAAiLUwLMaEH3R4gKwSN7WcRvcbnCS5sH9PzpqBnciwoA) To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): @@ -376,7 +378,7 @@ function RouterLink(props: LinkProps | AnchorProps) {
    Approach: Generic Components - Here is an example solution, see the further discussion for other solutions. *thanks to [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12#issuecomment-394440577)* +Here is an example solution, see the further discussion for other solutions. _thanks to [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12#issuecomment-394440577)_ ```tsx interface LinkProps {} @@ -397,12 +399,10 @@ return href="/">My link // ok to="/" href="/">My link // error -```` +```
    - -
    Approach: Composition @@ -434,7 +434,7 @@ const LinkButton: React.FunctionComponent = (props) => ( Login Login Login // Error: Property 'to' does not exist on type... -```` +```
    @@ -517,6 +517,7 @@ const UsageComponent: React.FC = () => ( ); ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) @@ -713,6 +714,7 @@ try { } } ``` + [View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing: @@ -1073,6 +1075,7 @@ export default function MyComponent({ label = "foobar" }: MyProps) { return
    Hello world {label}
    ; } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). From fa5fea4632bad41e39722423cd459bb9be04aab4 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2019 22:58:14 +0000 Subject: [PATCH 197/791] Prettify HOC.md --- HOC.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HOC.md b/HOC.md index d0e1563b..83b00c99 100644 --- a/HOC.md +++ b/HOC.md @@ -217,6 +217,7 @@ function BlogPost({ data, id }: BlogPostProps) { ); } ``` + [View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=15&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCgeirhgAskBnJOFIuxuMHMJuiHABGrYADsAVkgxIAJsICenVgAkA8gGEK4mEiiZ0rAOrAGAERQwUABV5MAPABUAfHADeFOHFmWUALjhHAG44Gm9fOGB+AHMkMT1gNAoAX2paR0j+BlYYBTBWCExwqzS4a0zlbjs4QsqAdygUMHz5NFxIeLF4BksK8Uw9IllSjQrstgwAVxQAG0iuvQM0AqLxhqaWuDbwCE74AApJlnkYQWjGoW8kA0mZmFsIPjhsXEiYAEoKJAAPSFhnyZiDDAXZwOqmegAZUmQiYaCgwDAMBBYicABoynAfroxLJ+CZzL4HnwnM4MRpnPtPKFaAwonQ8qxZjMIHV+EcBPMBlAyhihJN4OcUJdxrl8jUikZGs05Bp2rs4vAWGB2JYkDMlOCGBABW95rp9AkxNEwRDKv09HFlhKytSpRtZfK9gFkOgYAA6ABSkIAGgBRGZIECKuViJgwKCTDDQezWVwAMjgGjR1LCLEDGAsVgqKABQNOPMw0ECqdoPEe-Hprtkuw1wmkKCOohg+H4RBQNbEdfETGAshWlTQMxQTCY1PT0hgWf8cCp5C8Xh8VkhOqgywCYqQtWnK8ma6QKfnC-LfBdqE7Gvs3p97oAMsAhI0oAoALIoMQoWKyACCMAjD4FZh7GTOA1BAUxYwxAAiJcUCg5wEOpd44AAXlcRwKGQjwjzCcYQE-RIKkYIgAmvO8HyfV930-ORf3-fldH4cEZjmKwAGsci4TcbXtFo5R2PY2FxOAiCYCAZgAN2bfh+xuO4qgrUs2GiFAe26LgT34WoCXoacMTqehEnoCoJCOdSCgRaJxFmTFuK1Yz8Fg-ARKDCApPkF48FMNskAAR0mYAiGDLoxyPbjiX4FC4DI+9H3YKiPy-OiEQYoCQLAiDrGg2D4OcIJqW4yErF0VD3GpRdfACYJqWSfKjyIGA9zELZh1HOAdOnLFvhxPFEGID1+I6RVYzsDEirVVxsIXLZdnDSNoygfZNICCKsPKhcmEmfJFs0946umrw6SYd16HfWRAw0U7jVYKKjpOs6Lqu2J3SEcRZH2I69vWw7DOO8M1VKqaDoqqwAgnTNfH2HdV2WDFdu+uBavW1JKCPLxtiGrozD7F8dS6Ur9mQtC4GhvdlndDtZEu99YnvcM4j0D7fvu3FHpppAvtR6aMYVLoTBYgBVMQQDx+AosJ1DnAR0n93dIK3KQanrrpnFGbuq7zsVp6Obq9aNbZ66CaJqW0YXO6WBgcbdH2IHgdgsH1Unacod8Xd9wxO74dNrxkk59aiFxRm1u9mlKjFcQTSLHkmB4c8I84KJ3U0zJ3VTuApOfGbwEDb53XrcMwRQJRLPoeAxFZMZBFMgvuNMNh+HfBQEbCWDTRYuBw2AduRAZfI0EYNAOOGEOGqa2cEa8exeL4p1FWKFAULcc3iqQd1YOSdxU-dJnE+TkchIUd4N6oE3gc56aUZ9-bQ9HqBmo63w6pR6gACoX7gdRRiOGjTQYJNZ5CnAF+VAvi-GgPANoYZ4D8WCjAFWOloSwnhIiZEoIor2UQXCBESIURzi8DAxUKtDxeBdsuGGSAAjTkcIyY2JNXbkPdLEGABCQqE0wrrcgPw-gQNmvAAAQiyaI1gIDhgQTCLBKCUSlQweI5BODdh4LgAIiAQiREwGIbOGW646FWGofkOGdgAgZRgPYZRqjwwRWyr4eCxt1paNXkwsxwjwxLTsO6PsnxyB7SAA) @@ -402,6 +403,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { }; } ``` + [View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd5qQQkaY64BeOAbQF0A3BSq0GcAMK4WbADLAx3ABQBKLgD44iinDgAeACbAAbnAD0aisuHlq9RlNYwAykgA2SDNC6aA+gC44FBoATwAaOAgAdxoABRwwOgCg4NVODUUAb204YH0AqNj4ugA6XIoAX2UhG1F7ZkcAQQxgUW8VdU0s8h0UfX1JerYAxQYoANHgGgBzVI0maXZisABXOgALTLgYJAAPGHGYKHDcgPnHEvdpmDW4Soqq61sxSRoaD23+hzZvWzeMLW6cDObBc7k8R2ywJgTRgLXolkUAwWcgYD0o5FMpi2ayQdCQgSI2PxYCKWwgcAARvjJgArd5IfSU4JEuAACQA8uIKJNtlBMOh8QB1YDXJzLCl0NBQYBgWG0OIQBK6AAqGi6On0KBgKACyuq5QomGWNGatCBtD+MEUIBQYCc2u2yogCoSAQAYsbTTRwjawAAReRgLVoNZOl2JOAek1ymiqdVwIgwZZQGhwI3RuEq8IxOC7bY0fQcYWi8WS6WyuHhlVqcLiNQAnQ6QVQW1gBkDSBvIaIYgwYod2iOZXBNvV7Jx7I6GAj-Hh7wAKScAA1inIKS2oMEALJBFBTBkNGCHYAU5bbOi6cThdkgEW6GLhABEmu1j7UamqjbMWPERC1kymFlJjeKBzXAQc2GKOBlRxIEUFcNBllcLUGTgOdpzbOAcUJeQWUibD8WufEbSmYA0Cw1tWBKScEyQJMUyBZC6A4AcuxgYtQxxFhcz2VhCx7dA+1Yxx7yKNUaJ0FYKVcMjaILJAoHaeMvx0TFIzokMWRJRUOGCCBljgSIgngWl3igmDcOoJDGSpOB9EHQyRRuWxtj2HI7FQfRigkxsnngX0230e0ULnbhfWCx1nSKRRrnkYoGBQ8JYpKbSEjRFTfNqOAAoZAM6CDGAQ1C7LbTygqQzDaLkvih0kCStY4tSuh0oy79sUa0kmFxQJMF5IyoH4uhySIuDUwgIwFOlfRCNg6b+SQ+BB2owEMsTZNUwbVqdF0ZtKM+cC2J8jKMmKU7qqag0Vq2uATtOnKgtq8NLuuxtbuKe6yuDNYnqOxtzF+lqv2extyk-W59SAA) ## Docs Example: [Wrap the Display Name for Easy Debugging](https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging) From c27dee59210df69365cd72feb908e5bda5d20290 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 11 Oct 2019 22:58:18 +0000 Subject: [PATCH 198/791] Prettify README.md --- README.md | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index b6ec5567..07726f37 100644 --- a/README.md +++ b/README.md @@ -302,6 +302,7 @@ function TextInputWithFocusButton() { ); } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2&ssl=15&ssc=1&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) @@ -332,6 +333,7 @@ export function reducer(state: AppState, action: Action): AppState { } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) **Custom Hooks** @@ -348,6 +350,7 @@ export function useLoading() { return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) This way, when you destructure you actually get the right types based on destructure position. @@ -436,6 +439,7 @@ class App extends React.Component { } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) Don't forget that you can export/import/extend these types/interfaces for reuse. @@ -489,6 +493,7 @@ class App extends React.Component<{ message: string }, { count: number }> { }; } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=22&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) **Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: @@ -510,6 +515,7 @@ class App extends React.Component<{ } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=17&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). @@ -755,6 +761,7 @@ class Comp extends React.PureComponent { } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=2&ssc=28&pln=2&pc=22#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) ## Forms and Events @@ -798,6 +805,7 @@ class App extends React.Component< } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) Instead of typing the arguments and return values with `React.FormEvent<>` and `void`, you may alternatively apply types to the event handler itself (_contributed by @TomasHubelbauer_): @@ -852,6 +860,7 @@ 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/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&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), which is written in TypeScript. @@ -889,6 +898,7 @@ export function Component() { return
    {key}
    ; } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: @@ -929,6 +939,7 @@ export function Component() { ); } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=36&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. @@ -1041,6 +1052,7 @@ export class Modal extends React.Component { } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=36&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4QIATFAGwQgngF45mIaAK4gkNGADoA5khgBRNklHiAQgE8AkswAUAIias2AWhzddASjgo6cABIAVALIAZBUrEwA3BQD0Pq3R0IsA0UnAhcGoQQlBwABYwIGxwmMCK8dZW-MAAbnAA7sAwceHMBAbsJlww+N6U5EgAHpCwcGhs1jaOLOxwTTBizDbI6JIAwriQNB5wAN4UcH1sAFx2Tq6Kyrz8giIeEmhEKANuW3rMuRZ1C22TtB4AIsDM3ULi2pbz5IuLFRzVEhQYDAgzGcTSOmKwDoEiQbHMdUWAF8KDdqOB7uIAOppNgAVRoTDeMA+cxuvx6-24EiITBySDBEO0UJhcIRNxR9UWRBozCQUFJXx+cCIMBiNEQxBgDwA8o4DkcBgAFaAwdjM8EwsA4MAwtDgtjMHkAGjgLNh8MRcE5SKAA)
    @@ -1095,6 +1107,7 @@ class App extends React.Component< }; } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) **Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. If `A` and `B` are both object types, `A | B` isn't "either A or B", it is "A or B or both at once", which causes some confusion if you expected it to be the former. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: @@ -1109,7 +1122,8 @@ interface User { // Method 1: use `in` keyword function redirect(user: Admin | User) { - if("role" in user) { // use the `in` operator for typeguards since TS 2.7+ + if ("role" in user) { + // use the `in` operator for typeguards since TS 2.7+ routeToAdminPage(user.role); } else { routeToHomePage(user.email); @@ -1118,9 +1132,10 @@ function redirect(user: Admin | User) { // Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough function isAdmin(user: Admin | User): user is Admin { - return (user as any).role !== undefined + return (user as any).role !== undefined; } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. @@ -1190,6 +1205,7 @@ class MyComponent extends React.Component<{ } } ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgGU61gUAbAWSQGduUBzJABVa9ALwFuMKMAB2-fAG4KFOTCRRM6egAUcYbnADeFOHBA8+ggFxwpM+XAA+cAK6yAJkkxykH5eQAvirkaBCyUnAAwriQskiyMABMtsjoMAB0AGJRADx6EAYAfHASABRG5pYCSIEAlKUlZaZwuR7AAG5FLWa5ABYAjEVGFrw1gbkA9IPd5L2T7V0UdSFobCi8cBzUMeDhCfBIAB7qnoZpGBm7cQe5JnNVYzZ20nL8AYEl92ZEnhplDW+ZjgYQi8Eqoys9ECpTgMD6wG4GTA+m4AWBcCIMFcUFkcGaDwxuWu+0SSUeULEI2qgjgG0YzFYnBpwlEn2pT1qUxJ8TJswxdXRcGCQSAA) Note that you cannot assert your way to anything - basically it is only for refining types. Therefore it is not the same as "casting" a type. @@ -1266,6 +1282,7 @@ type HumanProps = { export const Human: React.FC = // ... export const Dog: React.FC = // ... ``` + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=20&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgCEUBnJABRzGbgF44BvCnGFoANi2YA5FCCQB+AFxxmMKMAB2AcwA0Q4Suqj5S5OhgA6AMIBlaxwh1YwJMz1x1MpEpVqtcAPT+cACurAAmcBpwAEYQMAAWFAC+VLT0ACIQmvZcvAJ6MCjAosyWEMHqMErqwSDRSFDJqXRwABK1KOo53HyC5MLxnWGl5ZXVtfWN5CnkSAAekLBwaBDqKm0d6ibEFgBilgA8TKzdcABkGyCd3QB8eQAUAJS8d-d6B2HAAG4BNxSPFAo80W8BWa3gmU02zM5n2RxY7E43AukNuD2ePFe70+P38f3IjyAA) Make sure not to confuse Intersection Types (which are **and** operations) with Union Types (which are **or** operations). From b20cf0efa14f6aebe046e52c6811c8f29bde39e6 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Oct 2019 16:26:43 -0700 Subject: [PATCH 199/791] feat: add jsjoeio as maintainer --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 07726f37..7bd788c1 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@
    -:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber), [@eps1lon](https://twitter.com/sebsilbermann) and [@IslamAttrash](https://twitter.com/IslamAttrash), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose)! :+1: +:wave: This repo is maintained by [@swyx](https://twitter.com/swyx), [@ferdaber](https://twitter.com/ferdaber), [@eps1lon](https://twitter.com/sebsilbermann), [@IslamAttrash](https://twitter.com/IslamAttrash), and [@jsjoeio](https://twitter.com/jsjoeio), we're so happy you want to try out TypeScript with React! If you see anything wrong or missing, please [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose)! :+1:
    From 7e3969124b7a63c093c3a2e87add359faabc2ef5 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Fri, 11 Oct 2019 16:29:21 -0700 Subject: [PATCH 200/791] fix: update contributors badge to show number --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7bd788c1..bede0195 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ --- -[![All Contributors](https://img.shields.io/badge/all_contributors-0-orange.svg?style=flat-square)](/CONTRIBUTORS.md) +[![All Contributors](https://img.shields.io/github/contributors/typescript-cheatsheets/react-typescript-cheatsheet?color=orange&style=flat-square)](/CONTRIBUTORS.md) ## All React + TypeScript Cheatsheets From fba06bac207003f3692cf9b236816e0cf4b09df3 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Sun, 13 Oct 2019 11:12:27 -0400 Subject: [PATCH 201/791] fix: update typescript links --- README.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index bede0195..30aea2ac 100644 --- a/README.md +++ b/README.md @@ -303,7 +303,7 @@ function TextInputWithFocusButton() { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2&ssl=15&ssc=1&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) @@ -334,7 +334,7 @@ export function reducer(state: AppState, action: Action): AppState { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) **Custom Hooks** @@ -351,7 +351,7 @@ export function useLoading() { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) This way, when you destructure you actually get the right types based on destructure position. @@ -440,7 +440,7 @@ class App extends React.Component { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) Don't forget that you can export/import/extend these types/interfaces for reuse. @@ -494,7 +494,7 @@ class App extends React.Component<{ message: string }, { count: number }> { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=22&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) **Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: @@ -516,7 +516,7 @@ class App extends React.Component<{ } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=17&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). @@ -762,7 +762,7 @@ class Comp extends React.PureComponent { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=2&ssc=28&pln=2&pc=22#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWOYAZwFEBHAVxQBs5tcD2IATFHQAWAOnpJWHMuQowAnmCRwAwizoxcANQ4tlAXjgoAdvIDcFYMZhIomdMoAKOMHTgBvCnDhgXAQQAuVXVNEB12PQtyAF9La1t7NGUAESRMKyR+AGUYFBsPLzgIGGFbHLykADFgJHZ+II0oKwBzKNjyBSU4cvzDVPTjTJ7lADJEJBgWKGMAFUUkAB5OpAhMOBgoEzpMaBBnCFcZiGGAPijMFmMMYAhjdc3jbd39w+PcmwAKXwO6IJe6ACUBXI3iIk2mwO83joKAAbpkXoEfC46KJvmA-AAaOAAehxcBh8K40DgICQIAgwAAXnkbsZCt5+LZgPDsu8kEF0aj0X5CtE2hQ0OwhG4VLgwHAkAAPGzGfhuZDoGCiRxTJBi8C3JDWBb-bGnSFwNC3RosDDQL4ov4ooGeEFQugsJRQS0-AFRKHrYT0UQaCpwQx2z3eYqlKDDaq1epwABEAEYAEwAZhjmIZUNEmY2Wx2UD2KKOw1drgB6f5fMKfpgwDQcGaE1STVZEZw+Z+xd+cD1BPZQWGtvTwDWH3ozDY7A7aP82KrSF9cIR-gBQLBUzuxhY7HYHqhq4h2ceubbryLXPdFZiQA) +[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) ## Forms and Events @@ -806,7 +806,7 @@ class App extends React.Component< } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2KA9Drg8IcMDjB1tcblwBccOjCjAeAcwDcmlRQB8W8ovso3HAAvL6KilYwtgBE0R7ulH5wepYAnmBOznAQPIgAkgDiABIAKnAAFij8dsB8SNmYIZo5YpUu9aEAFEi2QhgiAGLQIACiAG4ysqUAsgAyeTxgAK4wI9RIIDJeAJS2YxC1IT5KFjDlwHQidEgwAMowgUidSpacUewiaEtQRDwwJSgoM4biIxihqEt6iptglFCpYXBfnUoJ1tmFwkQYN9cp0LIpZHxgGMvHjwrInMt4DB0khgtFItE4GCIbSlGcLlcHtwRJEVNkeK0qsDgmzzpcWm1gXydCSkuE4LIdITiRYYR4KCogA) Instead of typing the arguments and return values with `React.FormEvent<>` and `void`, you may alternatively apply types to the event handler itself (_contributed by @TomasHubelbauer_): @@ -861,7 +861,7 @@ 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/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGctoRlM4BeRYmAOgFc6kLABQBKClVoM4AMSbs4o9gD4FFOHAA8mJmrhFMbAN7aozJJgC+u2gGVeAIxDAYRoUgBcndDxsBPGjAAFkgwwGgAogBuSAEiynCGuupI3GBE0QEAIuYovAA2MKIA3Elw1PTwMChQAOYh8ilVtfUodHAwvmBIEKyN1XXwAGQJpckgKMB5noZwkSh5vB5wDFDANDVwFiXk6rtwYK10AO7QACbTs-OLnitrG1ulDzu75VJI45PyTQPc7xN53DmCyQRTgAHowe1Okg0ME0ABrOgAQlKr3gBzoxzOX36IVShxOUFOgKuIPBkI6XVhMMRKOe6ghcBCaG4rN0Fis5CUug0p2AkW59M0eRQ9iQeUFe3U4Q+U1GmjWYF4lWhbAARH9Jmq4DQUCAkOrNXltWDJbsNGCRWKJTywXyBTz7Wb1BoreLnbsAAoEs7ueUaRXKqFddUYrFE7W6-Whn0R8Eei1um3PC1Ox38hOBlUhtV0BxOGDaoGLdUAGQgGzWJrNqYzFAtJhAgpEQA) +[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), which is written in TypeScript. @@ -899,7 +899,7 @@ export function Component() { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: @@ -940,7 +940,7 @@ export function Component() { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=36&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. @@ -1053,7 +1053,7 @@ export class Modal extends React.Component { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=36&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4QIATFAGwQgngF45mIaAK4gkNGADoA5khgBRNklHiAQgE8AkswAUAIias2AWhzddASjgo6cABIAVALIAZBUrEwA3BQD0Pq3R0IsA0UnAhcGoQQlBwABYwIGxwmMCK8dZW-MAAbnAA7sAwceHMBAbsJlww+N6U5EgAHpCwcGhs1jaOLOxwTTBizDbI6JIAwriQNB5wAN4UcH1sAFx2Tq6Kyrz8giIeEmhEKANuW3rMuRZ1C22TtB4AIsDM3ULi2pbz5IuLFRzVEhQYDAgzGcTSOmKwDoEiQbHMdUWAF8KDdqOB7uIAOppNgAVRoTDeMA+cxuvx6-24EiITBySDBEO0UJhcIRNxR9UWRBozCQUFJXx+cCIMBiNEQxBgDwA8o4DkcBgAFaAwdjM8EwsA4MAwtDgtjMHkAGjgLNh8MRcE5SKAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWRYmAEQHkBZObXAo9GAWgBNcZchTQQAdgGd4ICHxQAbBBAjwAvHAFoAriCRiYAOgDmSGAFF5SXfoBCATwCSfABQAiGXPk8cK1wEo4FAk4AAkAFWYAGQsrPRgAbgoAeiTAiQkdYDEjOCy4OwgtKDgACxgQeTZgS1KgwI1gADc4AHdgGBLcvgIPBW9lGHxE4XIkAA9qeDR5IODmWQU4cZg9PmDkbgMAYVxIMTi4AG8KOCX5AC5QiOjLazUNCG07gzQuFZi7tz4m-2GTuFE4HEcXowD48y0+mcAWO5FOp16igGBhQYDAqy2JWqLg6wAkBiQ8j8w1OAF8KP9AXs4gB1aryACqYhkkJg0KO-wRCyRKgMRBkjSQmOxzlx+MJxP+5JGpyIYj4SCg7Nh8LgRBgRTEtG4TGYLzeSAACtAYApRVj8WAcGB8WgsfI+HKADRwMUEokkuDS0lAA)
    @@ -1108,7 +1108,7 @@ class App extends React.Component< } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) **Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. If `A` and `B` are both object types, `A | B` isn't "either A or B", it is "A or B or both at once", which causes some confusion if you expected it to be the former. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: @@ -1136,7 +1136,7 @@ function isAdmin(user: Admin | User): user is Admin { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. @@ -1206,7 +1206,7 @@ class MyComponent extends React.Component<{ } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgGU61gUAbAWSQGduUBzJABVa9ALwFuMKMAB2-fAG4KFOTCRRM6egAUcYbnADeFOHBA8+ggFxwpM+XAA+cAK6yAJkkxykH5eQAvirkaBCyUnAAwriQskiyMABMtsjoMAB0AGJRADx6EAYAfHASABRG5pYCSIEAlKUlZaZwuR7AAG5FLWa5ABYAjEVGFrw1gbkA9IPd5L2T7V0UdSFobCi8cBzUMeDhCfBIAB7qnoZpGBm7cQe5JnNVYzZ20nL8AYEl92ZEnhplDW+ZjgYQi8Eqoys9ECpTgMD6wG4GTA+m4AWBcCIMFcUFkcGaDwxuWu+0SSUeULEI2qgjgG0YzFYnBpwlEn2pT1qUxJ8TJswxdXRcGCQSAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgGU61gUAbAWSQGduUBzJABVa9ALwFuMKMAB2-fAG4KFOTCRRM6egAUcYbnADeFOHBA8+ggFxwpM+XAA+cAK6yAJkkxykH5eQAvirkaBCyUnAAwriQskiyMABMtsjoMAB0AGJRADx6EAYAfHASABRG5pYCSIEAlKUlZaZwuR7AAG5FLWa5ABYAjEVGFrw1gbkA9IPd5L2T7V0UdSFobCi8cBzUMeDhCfBIAB7qnoZpGBm7cQe5JnNVYzZ20nL8AYEl92ZEnhplDW+ZjgYQi8Eqoys9ECpTgMD6wG4GTA+m4AWBcCIMFcUFkcGaDwxuWu+0SSUeULEI2qgjgG0YzFYnBpwlEn2pT1qUxJ8TJswxdXRcGCQSAA) Note that you cannot assert your way to anything - basically it is only for refining types. Therefore it is not the same as "casting" a type. @@ -1283,7 +1283,7 @@ export const Human: React.FC = // ... export const Dog: React.FC = // ... ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=1&ssc=1&pln=20&pc=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgCEUBnJABRzGbgF44BvCnGFoANi2YA5FCCQB+AFxxmMKMAB2AcwA0Q4Suqj5S5OhgA6AMIBlaxwh1YwJMz1x1MpEpVqtcAPT+cACurAAmcBpwAEYQMAAWFAC+VLT0ACIQmvZcvAJ6MCjAosyWEMHqMErqwSDRSFDJqXRwABK1KOo53HyC5MLxnWGl5ZXVtfWN5CnkSAAekLBwaBDqKm0d6ibEFgBilgA8TKzdcABkGyCd3QB8eQAUAJS8d-d6B2HAAG4BNxSPFAo80W8BWa3gmU02zM5n2RxY7E43AukNuD2ePFe70+P38f3IjyAA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgCEUBnJABRzGbgF44BvCnGFoANi2YA5FCCQB+AFxxmMKMAB2AcwA0Q4Suqj5S5OhgA6AMIBlaxwh1YwJMz1x1MpEpVqtcAPT+cACurAAmcBpwAEYQMAAWFAC+VLT0ACIQmvZcvAJ6MCjAosyWEMHqMErqwSDRSFDJqXRwABK1KOo53HyC5MLxnWGl5ZXVtfWN5CnkSAAekLBwaBDqKm0d6ibEFgBilgA8TKzdcABkGyCd3QB8eQAUAJS8d-d6B2HAAG4BNxSPFAo80W8BWa3gmU02zM5n2RxY7E43AukNuD2ePFe70+P38f3IjyAA) Make sure not to confuse Intersection Types (which are **and** operations) with Union Types (which are **or** operations). From 9631ce33a9ca14914743986e58d5788ea08faef3 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Sun, 13 Oct 2019 11:21:08 -0400 Subject: [PATCH 202/791] fix: update links in ADVANCED --- ADVANCED.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index d5ddc631..a66f55cd 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -170,7 +170,7 @@ function App() { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=20&ssc=2&pln=2&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) ## `as` props (passing a component to be rendered) @@ -294,7 +294,7 @@ const wrapper = ( ); ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615AL4UKAegAqOF4ILlgATwI0AAtgABstTXw4LQgkfjhqCHgkAA9gfngIajgYcK4CNg4wkIERUXwHOAC-CjRiwtZ2TnprOBE4PLptDIkvUUMwPn4lKp6oWqExSxtJdSIYAFcoEsN1GUEtYAA3UX2ZSSnQ-gdNHSh9U0nphxMQcx9yC4uJK4EHGLxRJIaifb6CPxHU7qcyeLyefwBCjCcrMfASOCAhKaJSFKA0ADmKlkBjgeMJxLuelJxlJ5OoBJW4npRLgXmSBUy2TgKH4-GABOoKAARnFmDAIKVUQRdLR8dR+WgAIIwGD44WbOgZABkXWqPGmghZDQc6lq9DKUVi2JByVS6S5OXynWKUoq+FlapoipVXo1WrgurmNUNxsaFBavnI7QV8AA7t0wn09l9+sHem8rAAiLUwLMaEH3R4gKwSN7WcRvcbnCS5sH9PzpqBnciwoA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): @@ -518,7 +518,7 @@ const UsageComponent: React.FC = () => ( ); ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) @@ -715,7 +715,7 @@ try { } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) +[View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing: @@ -1076,7 +1076,7 @@ export default function MyComponent({ label = "foobar" }: MyProps) { } ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). From 714861792f122912ce77e4b0d9a3427fc0fd6ec4 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Sun, 13 Oct 2019 11:25:59 -0400 Subject: [PATCH 203/791] fix: update links in HOC --- HOC.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HOC.md b/HOC.md index 83b00c99..71a14922 100644 --- a/HOC.md +++ b/HOC.md @@ -218,7 +218,7 @@ function BlogPost({ data, id }: BlogPostProps) { } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2&ssl=15&ssc=2&pln=3&pc=1#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCgeirhgAskBnJOFIuxuMHMJuiHABGrYADsAVkgxIAJsICenVgAkA8gGEK4mEiiZ0rAOrAGAERQwUABV5MAPABUAfHADeFOHFmWUALjhHAG44Gm9fOGB+AHMkMT1gNAoAX2paR0j+BlYYBTBWCExwqzS4a0zlbjs4QsqAdygUMHz5NFxIeLF4BksK8Uw9IllSjQrstgwAVxQAG0iuvQM0AqLxhqaWuDbwCE74AApJlnkYQWjGoW8kA0mZmFsIPjhsXEiYAEoKJAAPSFhnyZiDDAXZwOqmegAZUmQiYaCgwDAMBBYicABoynAfroxLJ+CZzL4HnwnM4MRpnPtPKFaAwonQ8qxZjMIHV+EcBPMBlAyhihJN4OcUJdxrl8jUikZGs05Bp2rs4vAWGB2JYkDMlOCGBABW95rp9AkxNEwRDKv09HFlhKytSpRtZfK9gFkOgYAA6ABSkIAGgBRGZIECKuViJgwKCTDDQezWVwAMjgGjR1LCLEDGAsVgqKABQNOPMw0ECqdoPEe-Hprtkuw1wmkKCOohg+H4RBQNbEdfETGAshWlTQMxQTCY1PT0hgWf8cCp5C8Xh8VkhOqgywCYqQtWnK8ma6QKfnC-LfBdqE7Gvs3p97oAMsAhI0oAoALIoMQoWKyACCMAjD4FZh7GTOA1BAUxYwxAAiJcUCg5wEOpd44AAXlcRwKGQjwjzCcYQE-RIKkYIgAmvO8HyfV930-ORf3-fldH4cEZjmKwAGsci4TcbXtFo5R2PY2FxOAiCYCAZgAN2bfh+xuO4qgrUs2GiFAe26LgT34WoCXoacMTqehEnoCoJCOdSCgRaJxFmTFuK1Yz8Fg-ARKDCApPkF48FMNskAAR0mYAiGDLoxyPbjiX4FC4DI+9H3YKiPy-OiEQYoCQLAiDrGg2D4OcIJqW4yErF0VD3GpRdfACYJqWSfKjyIGA9zELZh1HOAdOnLFvhxPFEGID1+I6RVYzsDEirVVxsIXLZdnDSNoygfZNICCKsPKhcmEmfJFs0946umrw6SYd16HfWRAw0U7jVYKKjpOs6Lqu2J3SEcRZH2I69vWw7DOO8M1VKqaDoqqwAgnTNfH2HdV2WDFdu+uBavW1JKCPLxtiGrozD7F8dS6Ur9mQtC4GhvdlndDtZEu99YnvcM4j0D7fvu3FHpppAvtR6aMYVLoTBYgBVMQQDx+AosJ1DnAR0n93dIK3KQanrrpnFGbuq7zsVp6Obq9aNbZ66CaJqW0YXO6WBgcbdH2IHgdgsH1Unacod8Xd9wxO74dNrxkk59aiFxRm1u9mlKjFcQTSLHkmB4c8I84KJ3U0zJ3VTuApOfGbwEDb53XrcMwRQJRLPoeAxFZMZBFMgvuNMNh+HfBQEbCWDTRYuBw2AduRAZfI0EYNAOOGEOGqa2cEa8exeL4p1FWKFAULcc3iqQd1YOSdxU-dJnE+TkchIUd4N6oE3gc56aUZ9-bQ9HqBmo63w6pR6gACoX7gdRRiOGjTQYJNZ5CnAF+VAvi-GgPANoYZ4D8WCjAFWOloSwnhIiZEoIor2UQXCBESIURzi8DAxUKtDxeBdsuGGSAAjTkcIyY2JNXbkPdLEGABCQqE0wrrcgPw-gQNmvAAAQiyaI1gIDhgQTCLBKCUSlQweI5BODdh4LgAIiAQiREwGIbOGW646FWGofkOGdgAgZRgPYZRqjwwRWyr4eCxt1paNXkwsxwjwxLTsO6PsnxyB7SAA) +[View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCgeirhgAskBnJOFIuxuMHMJuiHABGrYADsAVkgxIAJsICenVgAkA8gGEK4mEiiZ0rAOrAGAERQwUABV5MAPABUAfHADeFOHFmWUALjhHAG44Gm9fOGB+AHMkMT1gNAoAX2paR0j+BlYYBTBWCExwqzS4a0zlbjs4QsqAdygUMHz5NFxIeLF4BksK8Uw9IllSjQrstgwAVxQAG0iuvQM0AqLxhqaWuDbwCE74AApJlnkYQWjGoW8kA0mZmFsIPjhsXEiYAEoKJAAPSFhnyZiDDAXZwOqmegAZUmQiYaCgwDAMBBYicABoynAfroxLJ+CZzL4HnwnM4MRpnPtPKFaAwonQ8qxZjMIHV+EcBPMBlAyhihJN4OcUJdxrl8jUikZGs05Bp2rs4vAWGB2JYkDMlOCGBABW95rp9AkxNEwRDKv09HFlhKytSpRtZfK9gFkOgYAA6ABSkIAGgBRGZIECKuViJgwKCTDDQezWVwAMjgGjR1LCLEDGAsVgqKABQNOPMw0ECqdoPEe-Hprtkuw1wmkKCOohg+H4RBQNbEdfETGAshWlTQMxQTCY1PT0hgWf8cCp5C8Xh8VkhOqgywCYqQtWnK8ma6QKfnC-LfBdqE7Gvs3p97oAMsAhI0oAoALIoMQoWKyACCMAjD4FZh7GTOA1BAUxYwxAAiJcUCg5wEOpd44AAXlcRwKGQjwjzCcYQE-RIKkYIgAmvO8HyfV930-ORf3-fldH4cEZjmKwAGsci4TcbXtFo5R2PY2FxOAiCYCAZgAN2bfh+xuO4qgrUs2GiFAe26LgT34WoCXoacMTqehEnoCoJCOdSCgRaJxFmTFuK1Yz8Fg-ARKDCApPkF48FMNskAAR0mYAiGDLoxyPbjiX4FC4DI+9H3YKiPy-OiEQYoCQLAiDrGg2D4OcIJqW4yErF0VD3GpRdfACYJqWSfKjyIGA9zELZh1HOAdOnLFvhxPFEGID1+I6RVYzsDEirVVxsIXLZdnDSNoygfZNICCKsPKhcmEmfJFs0946umrw6SYd16HfWRAw0U7jVYKKjpOs6Lqu2J3SEcRZH2I69vWw7DOO8M1VKqaDoqqwAgnTNfH2HdV2WDFdu+uBavW1JKCPLxtiGrozD7F8dS6Ur9mQtC4GhvdlndDtZEu99YnvcM4j0D7fvu3FHpppAvtR6aMYVLoTBYgBVMQQDx+AosJ1DnAR0n93dIK3KQanrrpnFGbuq7zsVp6Obq9aNbZ66CaJqW0YXO6WBgcbdH2IHgdgsH1Unacod8Xd9wxO74dNrxkk59aiFxRm1u9mlKjFcQTSLHkmB4c8I84KJ3U0zJ3VTuApOfGbwEDb53XrcMwRQJRLPoeAxFZMZBFMgvuNMNh+HfBQEbCWDTRYuBw2AduRAZfI0EYNAOOGEOGqa2cEa8exeL4p1FWKFAULcc3iqQd1YOSdxU-dJnE+TkchIUd4N6oE3gc56aUZ9-bQ9HqBmo63w6pR6gACoX7gdRRiOGjTQYJNZ5CnAF+VAvi-GgPANoYZ4D8WCjAFWOloSwnhIiZEoIor2UQXCBESIURzi8DAxUKtDxeBdsuGGSAAjTkcIyY2JNXbkPdLEGABCQqE0wrrcgPw-gQNmvAAAQiyaI1gIDhgQTCLBKCUSlQweI5BODdh4LgAIiAQiREwGIbOGW646FWGofkOGdgAgZRgPYZRqjwwRWyr4eCxt1paNXkwsxwjwxLTsO6PsnxyB7SAA)
    @@ -404,7 +404,7 @@ function connect(mapStateToProps: Function, mapDispatchToProps: Function) { } ``` -[View in TypeScript Playground](https://www.typescriptlang.org/play/?noImplicitAny=false&strictNullChecks=false&strictFunctionTypes=false&strictPropertyInitialization=false&noImplicitThis=false&noImplicitReturns=false&alwaysStrict=false&target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd5qQQkaY64BeOAbQF0A3BSq0GcAMK4WbADLAx3ABQBKLgD44iinDgAeACbAAbnAD0aisuHlq9RlNYwAykgA2SDNC6aA+gC44FBoATwAaOAgAdxoABRwwOgCg4NVODUUAb204YH0AqNj4ugA6XIoAX2UhG1F7ZkcAQQxgUW8VdU0s8h0UfX1JerYAxQYoANHgGgBzVI0maXZisABXOgALTLgYJAAPGHGYKHDcgPnHEvdpmDW4Soqq61sxSRoaD23+hzZvWzeMLW6cDObBc7k8R2ywJgTRgLXolkUAwWcgYD0o5FMpi2ayQdCQgSI2PxYCKWwgcAARvjJgArd5IfSU4JEuAACQA8uIKJNtlBMOh8QB1YDXJzLCl0NBQYBgWG0OIQBK6AAqGi6On0KBgKACyuq5QomGWNGatCBtD+MEUIBQYCc2u2yogCoSAQAYsbTTRwjawAAReRgLVoNZOl2JOAek1ymiqdVwIgwZZQGhwI3RuEq8IxOC7bY0fQcYWi8WS6WyuHhlVqcLiNQAnQ6QVQW1gBkDSBvIaIYgwYod2iOZXBNvV7Jx7I6GAj-Hh7wAKScAA1inIKS2oMEALJBFBTBkNGCHYAU5bbOi6cThdkgEW6GLhABEmu1j7UamqjbMWPERC1kymFlJjeKBzXAQc2GKOBlRxIEUFcNBllcLUGTgOdpzbOAcUJeQWUibD8WufEbSmYA0Cw1tWBKScEyQJMUyBZC6A4AcuxgYtQxxFhcz2VhCx7dA+1Yxx7yKNUaJ0FYKVcMjaILJAoHaeMvx0TFIzokMWRJRUOGCCBljgSIgngWl3igmDcOoJDGSpOB9EHQyRRuWxtj2HI7FQfRigkxsnngX0230e0ULnbhfWCx1nSKRRrnkYoGBQ8JYpKbSEjRFTfNqOAAoZAM6CDGAQ1C7LbTygqQzDaLkvih0kCStY4tSuh0oy79sUa0kmFxQJMF5IyoH4uhySIuDUwgIwFOlfRCNg6b+SQ+BB2owEMsTZNUwbVqdF0ZtKM+cC2J8jKMmKU7qqag0Vq2uATtOnKgtq8NLuuxtbuKe6yuDNYnqOxtzF+lqv2extyk-W59SAA) +[View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd5qQQkaY64BeOAbQF0A3BSq0GcAMK4WbADLAx3ABQBKLgD44iinDgAeACbAAbnAD0aisuHlq9RlNYwAykgA2SDNC6aA+gC44FBoATwAaOAgAdxoABRwwOgCg4NVODUUAb204YH0AqNj4ugA6XIoAX2UhG1F7ZkcAQQxgUW8VdU0s8h0UfX1JerYAxQYoANHgGgBzVI0maXZisABXOgALTLgYJAAPGHGYKHDcgPnHEvdpmDW4Soqq61sxSRoaD23+hzZvWzeMLW6cDObBc7k8R2ywJgTRgLXolkUAwWcgYD0o5FMpi2ayQdCQgSI2PxYCKWwgcAARvjJgArd5IfSU4JEuAACQA8uIKJNtlBMOh8QB1YDXJzLCl0NBQYBgWG0OIQBK6AAqGi6On0KBgKACyuq5QomGWNGatCBtD+MEUIBQYCc2u2yogCoSAQAYsbTTRwjawAAReRgLVoNZOl2JOAek1ymiqdVwIgwZZQGhwI3RuEq8IxOC7bY0fQcYWi8WS6WyuHhlVqcLiNQAnQ6QVQW1gBkDSBvIaIYgwYod2iOZXBNvV7Jx7I6GAj-Hh7wAKScAA1inIKS2oMEALJBFBTBkNGCHYAU5bbOi6cThdkgEW6GLhABEmu1j7UamqjbMWPERC1kymFlJjeKBzXAQc2GKOBlRxIEUFcNBllcLUGTgOdpzbOAcUJeQWUibD8WufEbSmYA0Cw1tWBKScEyQJMUyBZC6A4AcuxgYtQxxFhcz2VhCx7dA+1Yxx7yKNUaJ0FYKVcMjaILJAoHaeMvx0TFIzokMWRJRUOGCCBljgSIgngWl3igmDcOoJDGSpOB9EHQyRRuWxtj2HI7FQfRigkxsnngX0230e0ULnbhfWCx1nSKRRrnkYoGBQ8JYpKbSEjRFTfNqOAAoZAM6CDGAQ1C7LbTygqQzDaLkvih0kCStY4tSuh0oy79sUa0kmFxQJMF5IyoH4uhySIuDUwgIwFOlfRCNg6b+SQ+BB2owEMsTZNUwbVqdF0ZtKM+cC2J8jKMmKU7qqag0Vq2uATtOnKgtq8NLuuxtbuKe6yuDNYnqOxtzF+lqv2extyk-W59SAA) ## Docs Example: [Wrap the Display Name for Easy Debugging](https://reactjs.org/docs/higher-order-components.html#convention-wrap-the-display-name-for-easy-debugging) From 4e43128aa400dd16682ee114bcb30fa8c46cc518 Mon Sep 17 00:00:00 2001 From: Joe Previte Date: Sun, 13 Oct 2019 11:32:31 -0400 Subject: [PATCH 204/791] fix: update CONTRIBUTING for TypeScript Playground links --- CONTRIBUTING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c3021e4d..eb21ae9d 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,5 +7,6 @@ I thought I should lay out some core principles that we will follow so that this 1. **We are a CHEATSHEET above all**: all examples to be as simple as possible, easily searched, and presented for copy-and-paste. 2. **Collapsible explanations**: No more than 1-2 sentences of explanation, any more than that we put inside `details` tags. 3. **React + TypeScript ONLY**: React's ecosystem is huge, we can't possibly cover it all. This includes Redux. Would encourage people to maintain separate lists for stuff like React + Apollo Graphql, for example. Also we make no attempt to convince people to use TypeScript, we only exist to help people who have already decided to try it out. +4. **Add TypeScript Playground Links**: Whenever adding a code example longer than four lines, add a link to the TypeScript Playground with the code. Use the default compiler Playground options. That's all I've got! Again, really happy you are thinking about helping out, who knows, the person who you might be helping is yourself in future! From 78ef3db947b9279bda0f31872b7c240d1e3d43de Mon Sep 17 00:00:00 2001 From: Guilherme Baron Date: Wed, 16 Oct 2019 14:21:13 -0300 Subject: [PATCH 205/791] docs: update ADVANCED.md --- ADVANCED.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index a66f55cd..7e96ae43 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1100,8 +1100,9 @@ You should check out large projects that are migrating from flow to pick up conc Useful libraries: -- https://github.com/bcherny/flow-to-typescript -- . +- +- +- If you have specific advice in this area, please file a PR! From c23a61c44f963dd585f860cb2345955c478ffa17 Mon Sep 17 00:00:00 2001 From: Guilherme Baron Date: Wed, 16 Oct 2019 14:31:48 -0300 Subject: [PATCH 206/791] fix: Khan/flow-to-ts link --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 7e96ae43..b091f67d 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1101,7 +1101,7 @@ You should check out large projects that are migrating from flow to pick up conc Useful libraries: - -- +- - If you have specific advice in this area, please file a PR! From 47ebb39155a957f9bedfdf8ea7c2afdaf3f40b6a Mon Sep 17 00:00:00 2001 From: Deniss Kozickis Date: Mon, 21 Oct 2019 23:21:09 +0300 Subject: [PATCH 207/791] docs(advancded): Fix format due to syntax error (#157) --- ADVANCED.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index b091f67d..77633222 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -382,23 +382,24 @@ Here is an example solution, see the further discussion for other solutions. _th ```tsx interface LinkProps {} -type AnchorProps = React.AnchorHTMLAttributes -type RouterLinkProps = Omit +type AnchorProps = React.AnchorHTMLAttributes; +type RouterLinkProps = Omit; const Link = ( -props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps + props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps ) => { -if ((props as RouterLinkProps).to) { -return -} else { -return
    -} -} - - to="/">My link // ok - href="/">My link // ok - to="/" href="/">My link // error + if ((props as RouterLinkProps).to) { + return ; + } else { + return ; + } +}; + to="/">My link; // ok + href="/">My link; // ok + to="/" href="/"> + My link +; // error ```
    From 2103006b89774d31877ed2e579cda2844d303c1f Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 21 Oct 2019 20:21:16 +0000 Subject: [PATCH 208/791] Prettify ADVANCED.md --- ADVANCED.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 77633222..d5c844ab 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -389,9 +389,9 @@ const Link = ( props: LinkProps & T extends RouterLinkProps ? RouterLinkProps : AnchorProps ) => { if ((props as RouterLinkProps).to) { - return ; + return ; } else { - return ; + return ; } }; From 28217c3f784748d353dbebf18fbc0dde55c3ec24 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Tue, 22 Oct 2019 13:16:23 +0530 Subject: [PATCH 209/791] Advanced Generic Class Components & getDerivedStateFromProps --- ADVANCED.md | 103 +++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 77 insertions(+), 26 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index d5c844ab..551e865e 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -53,6 +53,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Render Props](#render-props) - [Conditionally Rendering Components](#conditinonally-rendering-components) - [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered) + - [Generic Components](#generic-components) - [Typing a Component that Accepts Different Props](#typing-a-component-that-accepts-different-props) - [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both) - [Props: Must Pass Both](#props-must-pass-both) @@ -188,7 +189,7 @@ function PassThrough(props: { as: React.ElementType }) { ## Generic Components -Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. +Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use type `T` to annotate types of any variables defined inside your function. ```tsx interface Props { @@ -197,7 +198,7 @@ interface Props { } function List(props: Props) { const { items, renderItem } = props; - const [state, setState] = React.useState([]); + const [state, setState] = React.useState([]); // You can use type T in List function scope. return (
    {items.map(renderItem)} @@ -208,29 +209,6 @@ function List(props: Props) { } ``` -The same using fat arrow function style: - -```tsx -interface Props { - items: T[]; - renderItem: (item: T) => React.ReactNode; -} - -const List = (props: Props) => { - const { items, renderItem } = props; - const [state, setState] = React.useState([]); - return ( -
    - {items.map(renderItem)} - - {JSON.stringify(state, null, 2)} -
    - ); -}; -``` - -Note the `` before the function definition. You can't use just `` as it will confuse the TSX parser. - You can then use the generic components and get nice type safety through type inference: ```tsx @@ -260,7 +238,80 @@ ReactDOM.render( ); ``` -You can do this for [class components](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71) too (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)) +The same using fat arrow function style: + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} + +// Note the `` before the function definition. You can't use just `` as it will confuse the TSX parser. +const List = (props: Props) => { + const { items, renderItem } = props; + const [state, setState] = React.useState([]); // You can use type T in List function scope. + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(state, null, 2)} +
    + ); +}; +``` + +The same for using classes: (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)'s [gist](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71)) + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} + +interface State { + items: T[]; +} + +class List extends React.PureComponent, State> { + // You can use type T inside List class. + state: Readonly> = { + items: [] + }; + render() { + const { items, renderItem } = this.props; + // You can use type T inside List class. + const clone: T[] = items.slice(0); + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(this.state, null, 2)} +
    + ); + } +} +``` + +Though you can't use Generic Type Parameters for Static Members: + +```tsx +class List extends React.PureComponent, State> { + // Static members cannot reference class type parameters.ts(2302) + static getDerivedStateFromProps(props: Props, state: State) { + return { items: props.items }; + } +} +``` + +To fix this you need to convert your static function to a type inferred function: + +```tsx +class List extends React.PureComponent, State> { + static getDerivedStateFromProps(props: Props, state: State) { + return { items: props.items }; + } +} +``` ### Generic components with children From 9674a0aca4625ec7366a30b6238e812c3aadb7e6 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Tue, 22 Oct 2019 15:40:20 +0530 Subject: [PATCH 210/791] T -> Generic Type --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 551e865e..6c738219 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -189,7 +189,7 @@ function PassThrough(props: { as: React.ElementType }) { ## Generic Components -Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use type `T` to annotate types of any variables defined inside your function. +Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use the generic type to annotate types of any variables defined inside your function / class scope. ```tsx interface Props { From 4a63510e70871e416ad877b5c2a828a5d2f5a111 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Fri, 25 Oct 2019 09:06:16 +0530 Subject: [PATCH 211/791] extends unknown --- ADVANCED.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 6c738219..77f43cbb 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -238,7 +238,7 @@ ReactDOM.render( ); ``` -The same using fat arrow function style: +You can also use Generics using fat arrow function style: ```tsx interface Props { @@ -246,8 +246,10 @@ interface Props { renderItem: (item: T) => React.ReactNode; } -// Note the `` before the function definition. You can't use just `` as it will confuse the TSX parser. -const List = (props: Props) => { +// Note the before the function definition. +// You can't use just `` as it will confuse the TSX parser whether it's a JSX tag or a Generic Declaration. +// You can also use https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386 +const List = (props: Props) => { const { items, renderItem } = props; const [state, setState] = React.useState([]); // You can use type T in List function scope. return ( From d0aa24aba00aa043dd7f7bd0ca9e21e9cdea9961 Mon Sep 17 00:00:00 2001 From: Matijs Brinkhuis Date: Tue, 29 Oct 2019 09:17:39 +0100 Subject: [PATCH 212/791] Fix typo `NavLinkProps` did not exist in the example. --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index d5c844ab..c52a4199 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -411,7 +411,7 @@ If you want to conditionally render a component, sometimes is better to use [Rea ```tsx type AnchorProps = React.AnchorHTMLAttributes -type RouterLinkProps = Omit +type RouterLinkProps = Omit interface Button { as: React.ComponentClass | 'a' From 95640a5186161659ad1fe349b81d02d32c95aa22 Mon Sep 17 00:00:00 2001 From: Guilherme Baron Date: Wed, 30 Oct 2019 14:57:34 -0300 Subject: [PATCH 213/791] Add utility types link in HOC cheatsheet --- HOC.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/HOC.md b/HOC.md index 71a14922..35393c44 100644 --- a/HOC.md +++ b/HOC.md @@ -123,6 +123,8 @@ export function withTheme( Note that the `{...this.props as T}` assertion is needed because of a current bug in TS 3.2 https://github.com/Microsoft/TypeScript/issues/28938#issuecomment-450636046 +For `Optionalize` details check out the [utility types section](https://github.com/typescript-cheatsheets/typescript-utilities-cheatsheet#utility-types). + 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: ```tsx From a400a4ca2cff39004791ed468d19a5777b7e23a2 Mon Sep 17 00:00:00 2001 From: Mark Guckian Date: Thu, 31 Oct 2019 23:29:21 +0000 Subject: [PATCH 214/791] Add missing type in useReducer example --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 30aea2ac..b98ac2ef 100644 --- a/README.md +++ b/README.md @@ -312,6 +312,7 @@ example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#us You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. ```tsx +type AppState = {} type Action = | { type: "SET_ONE"; payload: string } | { type: "SET_TWO"; payload: number }; From 67d52ffc8ddf715faebcf6cafc89d425083a220e Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 1 Nov 2019 02:00:08 +0000 Subject: [PATCH 215/791] Prettify README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b98ac2ef..b5f03c6a 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,7 @@ example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#us You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. ```tsx -type AppState = {} +type AppState = {}; type Action = | { type: "SET_ONE"; payload: string } | { type: "SET_TWO"; payload: number }; From 969a02ce6d37b95c933c07df9f97070351300918 Mon Sep 17 00:00:00 2001 From: Deniss Kozickis Date: Sat, 2 Nov 2019 12:53:51 +0200 Subject: [PATCH 216/791] Fix link to Matterhorn --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index edf9a361..553ad916 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1295,7 +1295,7 @@ More `.eslintrc.json` options to consider with more options you may want for **a } ``` -You can read a [fuller TypeScript + ESLint setup guide here](https://github.com/MatterhornDev/matterhorn-posts/blob/learn-typescript-linting/learn-typescript-linting.md) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. +You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. ## Working with Non-TypeScript Libraries (writing your own index.d.ts) From ecdced39adf517862adc83a4dec5fb2df896dc8a Mon Sep 17 00:00:00 2001 From: Bryce Osterhaus Date: Tue, 5 Nov 2019 14:27:26 -0800 Subject: [PATCH 217/791] Add section for namespaced components --- ADVANCED.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 553ad916..b9c4ec76 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -73,6 +73,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Section 3: Misc. Concerns](#section-3-misc-concerns) - [Writing TypeScript Libraries instead of Apps](#writing-typescript-libraries-instead-of-apps) - [Commenting Components](#commenting-components) + - [Namespaced Components](#namespaced-components) - [Design System Development](#design-system-development) - [Migrating from Flow](#migrating-from-flow) - [Prettier](#prettier) @@ -1134,6 +1135,31 @@ export default function MyComponent({ label = "foobar" }: MyProps) { [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). +## Namespaced Components + +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()`; + +```tsx +import React from 'react' + +const Input = (props: any) => + +const Form = React.forwardRef(({children, ...otherProps}, ref) => ( +
    + {children} +
    +)); + +/** + * Exported components now can be used as `
    ` and `` + */ +export default Object.assign(Form, {Input: Input}); +``` + +[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) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + ## Design System Development 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) From 67bc07fdb1fbbeff1483b4825b710b32a71444aa Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 5 Nov 2019 18:56:39 -0500 Subject: [PATCH 218/791] add attribution --- ADVANCED.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index b9c4ec76..0479a1da 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1158,6 +1158,8 @@ export default Object.assign(Form, {Input: Input}); [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) +(Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165)) + [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). ## Design System Development From be9cb476fa176ee6f0b03f3ff0b5d77cc23311f3 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Tue, 5 Nov 2019 23:57:10 +0000 Subject: [PATCH 219/791] Prettify ADVANCED.md --- ADVANCED.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 0479a1da..9f4d5c00 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1140,20 +1140,22 @@ export default function MyComponent({ label = "foobar" }: MyProps) { 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()`; ```tsx -import React from 'react' +import React from "react"; -const Input = (props: any) => +const Input = (props: any) => ; -const Form = React.forwardRef(({children, ...otherProps}, ref) => ( - - {children} - -)); +const Form = React.forwardRef( + ({ children, ...otherProps }, ref) => ( +
    + {children} +
    + ) +); /** * Exported components now can be used as `
    ` and `` */ -export default Object.assign(Form, {Input: Input}); +export default Object.assign(Form, { Input: Input }); ``` [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) From f81211ed2e602a9c506573ae55b6f5e3543977f4 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 18 Nov 2019 11:15:38 +0800 Subject: [PATCH 220/791] update recommended tsconfig for apps --- README.md | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index b5f03c6a..0d085c66 100644 --- a/README.md +++ b/README.md @@ -1481,28 +1481,33 @@ This is not yet written. Please PR or [File an issue](https://github.com/typescr # Troubleshooting Handbook: tsconfig.json -You can find [all the Compiler options in the Typescript docs](https://www.typescriptlang.org/docs/handbook/compiler-options.html). This is the setup I roll with for my component library: +You can find [all the Compiler options in the Typescript docs](https://www.typescriptlang.org/docs/handbook/compiler-options.html). This is the setup I roll with for APPS (not libraries - for libraries you may wish to see the settings we use in `tsdx`): ```json { "compilerOptions": { + "incremental": true, "outDir": "build/lib", - "module": "commonjs", "target": "es5", - "lib": ["es5", "es6", "es7", "es2017", "dom"], + "module": "esnext", + "lib": ["dom", "esnext"], "sourceMap": true, + "importHelpers": true, + "declaration": true, + "rootDir": "src", + "strict": true, + "alwaysStrict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, "allowJs": false, "jsx": "react", "moduleResolution": "node", - "rootDir": "src", "baseUrl": "src", "forceConsistentCasingInFileNames": true, - "noImplicitReturns": true, - "strict": true, "esModuleInterop": true, "suppressImplicitAnyIndexErrors": true, - "noUnusedLocals": true, - "declaration": true, "allowSyntheticDefaultImports": true, "experimentalDecorators": true }, From e5a15a2b4e45eef344903e09cae9ec7dcf069246 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 25 Nov 2019 12:24:31 +0800 Subject: [PATCH 221/791] add new type extraction tip --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 0d085c66..5f7a3276 100644 --- a/README.md +++ b/README.md @@ -1404,6 +1404,39 @@ function foo(bar: string) { type FooReturn = ReturnType; // { baz: number } ``` +In fact you can grab virtually anything public: [see this blogpost from Ivan Koshelev](http://ikoshelev.azurewebsites.net/search/id/11/Pragmatic-uses-of-TypeScript-type-system-My-type-of-type) + +```ts +function foo() { + return { + a: 1, + b: 2, + subInstArr: [{ + c: 3, + d: 4 + }] + } +} + +type InstType = ReturnType +type SubInstArr = InstType['subInstArr']; +type SubIsntType = SubInstArr[0]; + +let baz: SubIsntType = { + c: 5, + d: 6 // type checks ok! +} + +//You could just write a one-liner, +//But please make sure it is forward-readable +//(you can understand it from reading once left-to-right with no jumps) +type SubIsntType2 = ReturnType['subInstArr'][0]; +let baz2: SubIsntType2 = { + c: 5, + d: 6 // type checks ok! +} +``` + # Troubleshooting Handbook: Images and other non-TS/TSX files Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): From 88872dc92b9f1005a3286e54caaa36578d3469ec Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2019 04:24:37 +0000 Subject: [PATCH 222/791] Prettify README.md --- README.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 5f7a3276..145a8652 100644 --- a/README.md +++ b/README.md @@ -1411,30 +1411,32 @@ function foo() { return { a: 1, b: 2, - subInstArr: [{ + subInstArr: [ + { c: 3, d: 4 - }] - } + } + ] + }; } -type InstType = ReturnType -type SubInstArr = InstType['subInstArr']; +type InstType = ReturnType; +type SubInstArr = InstType["subInstArr"]; type SubIsntType = SubInstArr[0]; let baz: SubIsntType = { c: 5, d: 6 // type checks ok! -} +}; //You could just write a one-liner, //But please make sure it is forward-readable //(you can understand it from reading once left-to-right with no jumps) -type SubIsntType2 = ReturnType['subInstArr'][0]; +type SubIsntType2 = ReturnType["subInstArr"][0]; let baz2: SubIsntType2 = { c: 5, d: 6 // type checks ok! -} +}; ``` # Troubleshooting Handbook: Images and other non-TS/TSX files From 3a66bc0e22d12873333c207f702c057fe6b08ca3 Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 25 Nov 2019 14:34:10 +0800 Subject: [PATCH 223/791] Update MIGRATING.md --- MIGRATING.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MIGRATING.md b/MIGRATING.md index 2cf710cf..cd6c4861 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -201,6 +201,9 @@ Old content that is possibly out of date - 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) - [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 +- Quick-n-dirty [Flow to TS Codemod](https://gist.github.com/skovhus/c57367ce6ecbc3f70bb7c80f25727a11) +- [Ecobee's brief experience](https://mobile.twitter.com/alanhietala/status/1104450494754377728) ## Results From 37a227a0d9fa2564ad07fa99eec916429db0cc89 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 27 Nov 2019 00:49:30 -0500 Subject: [PATCH 224/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index cd6c4861..95b2965d 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -204,6 +204,7 @@ Old content that is possibly out of date - Retail-UI's Codemod: https://github.com/skbkontur/retail-ui/tree/master/packages/react-ui-codemodes/flow-to-ts - Quick-n-dirty [Flow to TS Codemod](https://gist.github.com/skovhus/c57367ce6ecbc3f70bb7c80f25727a11) - [Ecobee's brief experience](https://mobile.twitter.com/alanhietala/status/1104450494754377728) +- [Migrating a 50K SLOC Flow + React Native app to TypeScript](https://blog.usejournal.com/migrating-a-flow-react-native-app-to-typescript-c74c7bceae7d) ## Results From efc4b9d8ff9024b75381beeb7e182e1810bdcc5c Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 28 Nov 2019 03:56:27 -0500 Subject: [PATCH 225/791] add forwardref note https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/167 --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 145a8652..96c17e1e 100644 --- a/README.md +++ b/README.md @@ -1027,6 +1027,8 @@ If you are grabbing the props of a component that forwards refs, use [`Component More info: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 +You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/167). + [Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). ## Portals From ca1f16cca58dbf3a83decd6812f8d839bb5ddc5e Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 4 Dec 2019 03:47:57 -0500 Subject: [PATCH 226/791] spanish translation! --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 96c17e1e..2df99c12 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ [**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) From 41cbcbeebf28a55e8be912ae687866e3acff71ac Mon Sep 17 00:00:00 2001 From: hazem osama ibrahim Date: Fri, 6 Dec 2019 08:08:12 +0200 Subject: [PATCH 227/791] update CRA TypeScript template command (#169) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2df99c12..ab5693ff 100644 --- a/README.md +++ b/README.md @@ -116,7 +116,7 @@ This guide will always assume you are starting with the latest TypeScript versio ## React + TypeScript Starter Kits -1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app my-new-react-typescript-app --typescript` +1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app my-app --template typescript` - We used to recommend `create-react-app-typescript` but it is now [deprecated](https://www.reddit.com/r/reactjs/comments/a5919a/createreactapptypescript_has_been_archived_rip/). [see migration instructions](https://vincenttunru.com/migrate-create-react-app-typescript-to-create-react-app/) From de2df7158e62f8c687e9ff943350ae2d77b3d5d6 Mon Sep 17 00:00:00 2001 From: JavaScript Joe Date: Sun, 8 Dec 2019 10:31:15 -0700 Subject: [PATCH 228/791] fix: remove unnecessary tsconfig option --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index ab5693ff..c3ce98af 100644 --- a/README.md +++ b/README.md @@ -1534,7 +1534,6 @@ You can find [all the Compiler options in the Typescript docs](https://www.types "declaration": true, "rootDir": "src", "strict": true, - "alwaysStrict": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, From e54491e0ad6dfdf1c6d80a8e38afeba44a01c20e Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 12 Dec 2019 14:47:22 +0800 Subject: [PATCH 229/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 95b2965d..81dc7052 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -116,6 +116,7 @@ Problems to be aware of: - [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) ([used by Codecademy](https://mobile.twitter.com/JoshuaKGoldberg/status/1159090281314160640)) - [TypeWiz](https://github.com/urish/typewiz) +- [js-to-ts-converter](https://github.com/gregjacobs/js-to-ts-converter) ### Manual JS to TS Conversion From a3e5f4dbacafe9ac5d300c402945b05e60b8ff0e Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 13 Dec 2019 09:22:37 -0500 Subject: [PATCH 230/791] Update MIGRATING.md --- MIGRATING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING.md b/MIGRATING.md index 81dc7052..39bfd480 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -51,7 +51,7 @@ Read [TypeScript's official Guide for migrating from JS](https://www.typescriptl Misc tips/approaches successful companies have taken - `@ts-ignore` on compiler errors for libraries with no typedefs -- 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)) +- 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). - 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])
    From 79bba713b98cbaa98b7c22f22d2a78d230ce5399 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 13 Dec 2019 09:23:21 -0500 Subject: [PATCH 231/791] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3ce98af..98793a6c 100644 --- a/README.md +++ b/README.md @@ -1497,7 +1497,7 @@ these are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty # Troubleshooting Handbook: TSLint -**Note: TSLint is in maintenance and ESLint is the way forward for TypeScript** +**Note: TSLint is in maintenance and ESLint is the way forward for TypeScript. [You can convert TSlint to ESlint with this tool](https://github.com/typescript-eslint/tslint-to-eslint-config).** Sometimes TSLint is just getting in the way. Judiciously turning off of things can be helpful. Here are useful tslint disables you may use: From 9548e64bb2a8a106d5d2f22362d42b96948db577 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 13 Dec 2019 09:23:33 -0500 Subject: [PATCH 232/791] Update ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 9f4d5c00..906951e1 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1231,7 +1231,7 @@ This is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files) ## 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. +> ⚠️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). > ⚠️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. From 6b519ad94be07feb4ce1f72d4705e72f62a30a80 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 13 Dec 2019 09:27:05 -0500 Subject: [PATCH 233/791] Update ADVANCED.md --- ADVANCED.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 906951e1..b8c7bd49 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -77,6 +77,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Design System Development](#design-system-development) - [Migrating from Flow](#migrating-from-flow) - [Prettier](#prettier) + - [Testing](#testing) - [Linting](#linting) - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) @@ -1229,6 +1230,14 @@ yarn add -D prettier husky lint-staged This is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). +## Testing + +Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions: + +- https://github.com/azz/jest-runner-tsc +- https://github.com/SamVerschueren/tsd +- https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) + ## 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). From 996d1c81231d04237e5106e72a00ced3a28799d5 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 13 Dec 2019 09:39:36 -0500 Subject: [PATCH 234/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 39bfd480..72a5fb63 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -228,6 +228,7 @@ Open Source - [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) - [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) +- [Google Workbox migration](https://github.com/GoogleChrome/workbox/pull/2058) - [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) - [React Native CLI](https://github.com/react-native-community/cli/issues/683) From b7286e92b6793f55d4cabff7571c2e1581b9f319 Mon Sep 17 00:00:00 2001 From: JavaScript Joe Date: Sat, 14 Dec 2019 14:50:30 -0700 Subject: [PATCH 235/791] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 98793a6c..73d2b1aa 100644 --- a/README.md +++ b/README.md @@ -310,7 +310,7 @@ example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#us **useReducer** -You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. +You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. ```tsx type AppState = {}; From 4b152dd6bd77cc4ffdae0da095fd349a4c6a599f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dante=20Calder=C3=B3n?= Date: Mon, 16 Dec 2019 23:14:45 -0500 Subject: [PATCH 236/791] Add syntax highlight for js code --- MIGRATING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING.md b/MIGRATING.md index 72a5fb63..3a216b2a 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -64,7 +64,7 @@ Webpack tips - 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)) - Webpack config: -``` +```js module.exports = { resolve: { From 18ba9e019891aa0dae16973eaf2f8299edf39dae Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 20 Dec 2019 12:07:15 -0500 Subject: [PATCH 237/791] swap out tslint for eslint in basic hadbook --- README.md | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 73d2b1aa..338ef7d7 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ - [The Types I need Weren't Exported!](#the-types-i-need-werent-exported) - [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) - [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilties) -- [Troubleshooting Handbook: TSLint](#troubleshooting-handbook-tslint) +- [Troubleshooting Handbook: ESLint](#troubleshooting-handbook-eslint) - [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) - [Recommended React + TypeScript codebases to learn from](#recommended-react--typescript-codebases-to-learn-from) - [Recommended React + TypeScript talks](#recommended-react--typescript-talks) @@ -1495,27 +1495,13 @@ these are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty - `Required`: Make all properties in an object required - `ReturnType` A function's return type -# Troubleshooting Handbook: TSLint +# Troubleshooting Handbook: ESLint **Note: TSLint is in maintenance and ESLint is the way forward for TypeScript. [You can convert TSlint to ESlint with this tool](https://github.com/typescript-eslint/tslint-to-eslint-config).** -Sometimes TSLint is just getting in the way. Judiciously turning off of things can be helpful. Here are useful tslint disables you may use: +This section needs writing, but you can probably find a good starting point with [Wes Bos' ESLint config](https://github.com/wesbos/eslint-config-wesbos) (which comes with a [YouTube intro](https://www.youtube.com/watch?v=lHAeK8t94as)). -- `/* tslint:disable */` total disable -- `// tslint:disable-line` just this line -- `/* tslint:disable:semicolon */` sometimes prettier adds semicolons and tslint doesn't like it. -- `/* tslint:disable:no-any */` disable tslint restriction on no-any when you WANT to use any -- `/* tslint:disable:max-line-length */` disable line wrapping linting - -so on and so forth. there are any number of things you can disable, usually you can look at the error raised in VScode or whatever the tooling and the name of the error will correspond to the rule you should disable. - -
    - -Explanation - -This is not yet written. Please PR or [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new) with your suggestions! - -
    +Then, check out the [Prettier](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#prettier) and [Linting](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#linting) sections of the ADVANCED cheatsheet! # Troubleshooting Handbook: tsconfig.json From d2920e9132ab6fd2b8b860a7e940c018ac5e8167 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 20 Dec 2019 12:43:15 -0500 Subject: [PATCH 238/791] add notes for TS 3.5 and 3.6 and 3.7 --- ADVANCED.md | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 102 insertions(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index b8c7bd49..7d02a04d 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -70,6 +70,9 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [TypeScript 3.2](#typescript-32) - [TypeScript 3.3](#typescript-33) - [TypeScript 3.4](#typescript-34) + - [TypeScript 3.5](#typescript-35) + - [TypeScript 3.6](#typescript-36) + - [TypeScript 3.7](#typescript-37) - [Section 3: Misc. Concerns](#section-3-misc-concerns) - [Writing TypeScript Libraries instead of Apps](#writing-typescript-libraries-instead-of-apps) - [Commenting Components](#commenting-components) @@ -1082,10 +1085,108 @@ const GenericComponent2 = myHoc(GenericComponent); See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272) -## TypeScript Roadmap + +## TypeScript 3.6 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)] + +Nothing particularly React specific but [the playground](https://github.com/agentcooper/typescript-play) got an upgrade and [Ambient Classes and Functions Can Merge](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge) + + +## TypeScript 3.7 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)] + +1. Optional Chaining + +```ts +let x = foo?.bar.baz(); + +// is equivalent to + +let x = (foo === null || foo === undefined) ? undefined : foo.bar.baz(); + +// Optional Element access +function tryGetFirstElement(arr?: T[]) { + return arr?.[0]; +} + +// Optional Call +async function makeRequest(url: string, log?: (msg: string) => void) { + log?.(`Request started at ${new Date().toISOString()}`); + const result = (await fetch(url)).json(); + log?.(`Request finished at at ${new Date().toISOString()}`); + return result; +} +``` + +2. Nullish Coalescing + +```ts +let x = foo ?? bar(); + +// equivalent to + +let x = (foo !== null && foo !== undefined) ? foo : bar(); +``` + +**YOU SHOULD USUALLY USE `??` WHEREVER YOU NORMALLY USE `||`** unless you truly mean falsiness: + +```tsx +function ShowNumber({ value }: { value: number }) { + let _value = value || 0.5 // will replace 0 with 0.5 even if user really means 0 + // etc... +} +``` + +3. Assertion Functions + +```tsx +function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new AssertionError(msg) + } +} +function yell(str) { + assert(typeof str === "string"); + + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? +} +``` + +You can also assert without a custom function: + +```tsx +function assertIsString(val: any): asserts val is string { + if (typeof val !== "string") { + throw new AssertionError("Not a string!"); + } +} +function yell(str: any) { + assertIsString(str); + + // Now TypeScript knows that 'str' is a 'string'. + + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? +} +``` + +4. `ts-nocheck` + +You can now add `// @ts-nocheck` to the top of TypeScript files! good for migrations. + +## TypeScript Roadmap and Spec https://github.com/Microsoft/TypeScript/wiki/Roadmap +Did you also know you can read the TypeScript spec online?? https://github.com/microsoft/TypeScript/blob/master/doc/spec.md + # Section 3: Misc. Concerns 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. From 314d3a67f7e3354d420532eb8caa8813a622a777 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 20 Dec 2019 17:43:19 +0000 Subject: [PATCH 239/791] Prettify ADVANCED.md --- ADVANCED.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 7d02a04d..27347500 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1085,14 +1085,12 @@ const GenericComponent2 = myHoc(GenericComponent); See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272) - ## TypeScript 3.6 [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)] Nothing particularly React specific but [the playground](https://github.com/agentcooper/typescript-play) got an upgrade and [Ambient Classes and Functions Can Merge](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge) - ## TypeScript 3.7 [[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)] @@ -1105,7 +1103,7 @@ let x = foo?.bar.baz(); // is equivalent to let x = (foo === null || foo === undefined) ? undefined : foo.bar.baz(); - + // Optional Element access function tryGetFirstElement(arr?: T[]) { return arr?.[0]; @@ -1125,7 +1123,7 @@ async function makeRequest(url: string, log?: (msg: string) => void) { ```ts let x = foo ?? bar(); -// equivalent to +// equivalent to let x = (foo !== null && foo !== undefined) ? foo : bar(); ``` @@ -1134,7 +1132,7 @@ let x = (foo !== null && foo !== undefined) ? foo : bar(); ```tsx function ShowNumber({ value }: { value: number }) { - let _value = value || 0.5 // will replace 0 with 0.5 even if user really means 0 + let _value = value || 0.5; // will replace 0 with 0.5 even if user really means 0 // etc... } ``` From 47df58b83b71caaad0bfcfe37cfaff0bec16cac8 Mon Sep 17 00:00:00 2001 From: alvinhui Date: Sun, 22 Dec 2019 22:35:13 +0800 Subject: [PATCH 240/791] chore: add hooks anchor --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 338ef7d7..642a0d2b 100644 --- a/README.md +++ b/README.md @@ -999,7 +999,7 @@ const Consumer = Context.Consumer; ## forwardRef/createRef -Check the [Hooks section](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md) for `useRef`. +Check the [Hooks section](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/README.md#hooks) for `useRef`. `createRef`: From dd6e9a8f48c61736813eb2d039a9c180633e70bc Mon Sep 17 00:00:00 2001 From: swyx Date: Mon, 23 Dec 2019 11:42:30 -0500 Subject: [PATCH 241/791] Update ADVANCED.md --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 27347500..e47dc50d 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -855,7 +855,7 @@ Sometimes DefinitelyTyped can get it wrong, or isn't quite addressing your use c TypeScript Versions often introduce new ways to do things; this section helps current users of React + TypeScript upgrade TypeScript versions and explore patterns commonly used by TypeScript + React apps and libraries. This may have duplications with other sections; if you spot any discrepancies, [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new)! -_TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!_ Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). +_TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!_ Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). For more TypeScript history, see [A Brief History of TypeScript Types](https://github.com/blakeembrey/a-brief-history-of-types-with-typescript) and [A Brief History of DefinitelyTyped](https://blog.johnnyreilly.com/2019/10/definitely-typed-movie.html) ## TypeScript 2.9 From b402feda6830bc115e94681bde57d8923bfbf012 Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 31 Dec 2019 14:02:36 -0500 Subject: [PATCH 242/791] add link to ts docs flags --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 642a0d2b..50a54ced 100644 --- a/README.md +++ b/README.md @@ -145,8 +145,7 @@ import ReactDOM from "react-dom"; Why `allowSyntheticDefaultImports` over `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) has said that it's better for webpack/parcel. For more discussion check out -Please PR or [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new) with your suggestions! - +You should also check [the new TypeScript docs for official descriptions between each compiler flag](https://www.typescriptlang.org/v2/en/tsconfig#allowSyntheticDefaultImports)!
    # Section 2: Getting Started @@ -1505,7 +1504,7 @@ Then, check out the [Prettier](https://github.com/typescript-cheatsheets/react-t # Troubleshooting Handbook: tsconfig.json -You can find [all the Compiler options in the Typescript docs](https://www.typescriptlang.org/docs/handbook/compiler-options.html). This is the setup I roll with for APPS (not libraries - for libraries you may wish to see the settings we use in `tsdx`): +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/v2/en/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`): ```json { From 20ddc77425b9798add240c0b4d1707ee1d0820f4 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Tue, 31 Dec 2019 19:02:41 +0000 Subject: [PATCH 243/791] Prettify README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 50a54ced..6fe35585 100644 --- a/README.md +++ b/README.md @@ -146,6 +146,7 @@ import ReactDOM from "react-dom"; Why `allowSyntheticDefaultImports` over `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) has said that it's better for webpack/parcel. For more discussion check out You should also check [the new TypeScript docs for official descriptions between each compiler flag](https://www.typescriptlang.org/v2/en/tsconfig#allowSyntheticDefaultImports)! + # Section 2: Getting Started From b39ea90322f52fc666d09552c800685c0597e212 Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 4 Jan 2020 07:57:54 +0800 Subject: [PATCH 244/791] add note on `infer` keyword --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 6fe35585..de31d34a 100644 --- a/README.md +++ b/README.md @@ -1442,6 +1442,9 @@ let baz2: SubIsntType2 = { }; ``` +- TS also ships with a `Parameters` utility type for extracting the parameters of a function +- for anything more "custom", the `infer` keyword is the basic building block for this, but takes a bit of getting used to. Look at the source code for the above utility types, and [this example](https://twitter.com/mgechev/status/1211030455224422401?s=20) to get the idea. + # Troubleshooting Handbook: Images and other non-TS/TSX files Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): From 9c81e5cc33d88cbcc759366f7b3f0ea8e2aba41a Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 3 Jan 2020 19:20:40 -0500 Subject: [PATCH 245/791] Update MIGRATING.md --- MIGRATING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/MIGRATING.md b/MIGRATING.md index 3a216b2a..3a12e80a 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -214,6 +214,12 @@ Old content that is possibly out of date - Found incorrect function calls for [Tiny][tiny] - Found rarely used, buggy code that was untested for [Tiny][tiny] +## Academic Studies of Migration + +- [To Type or Not to Type: Quantifying Detectable Bugs in JavaScript](http://earlbarr.com/publications/typestudy.pdf) + +> 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%! + ## Misc migration stories by notable companies and open source - [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) From ac018eb0e19f49b90a30fc052c3a6769a2ba1975 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 3 Jan 2020 19:27:02 -0500 Subject: [PATCH 246/791] add strong recommendation for ts playground --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de31d34a..2ebdec1a 100644 --- a/README.md +++ b/README.md @@ -109,8 +109,9 @@ ## Prerequisites 1. good understanding of [React](https://reactjs.org) -2. familiarity with [TypeScript Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful) +2. familiarity with [TypeScript Basic Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful) 3. having read [the TypeScript section in the official React docs](https://reactjs.org/docs/static-type-checking.html#typescript). +4. having read [the React section of the new TypeScript playground](http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react) (optional: also step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section) This guide will always assume you are starting with the latest TypeScript version. Notes for older versions will be in expandable `
    ` tags. @@ -1679,6 +1680,7 @@ Believe it or not, we have only barely introduced TypeScript here in this cheats It is worth mentioning some resources to help you get started: +- Step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section, written by @Orta - 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.gitbooks.io/typescript/ From 58d3c6a6c8f3d05f06c4cec2354aafe72206a7e8 Mon Sep 17 00:00:00 2001 From: JavaScript Joe Date: Fri, 3 Jan 2020 20:12:30 -0700 Subject: [PATCH 247/791] recommend explicit return types --- ADVANCED.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index e47dc50d..04eb6eda 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1383,12 +1383,13 @@ and a suitable `.eslintrc.json`: "error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false } ], + "@typescript-eslint/explicit-function-return-type": "warn", "no-empty": "warn" } } ``` -This is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. +Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. More `.eslintrc.json` options to consider with more options you may want for **apps**: @@ -1435,6 +1436,8 @@ More `.eslintrc.json` options to consider with more options you may want for **a You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. +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. + ## Working with Non-TypeScript Libraries (writing your own index.d.ts) Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: From 72011e86c0b2a051f04c32352ad064ef8d82a191 Mon Sep 17 00:00:00 2001 From: swyx Date: Sat, 4 Jan 2020 23:49:22 -0500 Subject: [PATCH 248/791] Update ADVANCED.md --- ADVANCED.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 04eb6eda..fe11a994 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1357,10 +1357,10 @@ add a `lint` script to your `package.json`: }, ``` -and a suitable `.eslintrc.json`: +and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comments): -```json -{ +```js +module.exports = { "env": { "es6": true, "node": true, @@ -1383,7 +1383,7 @@ and a suitable `.eslintrc.json`: "error", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false } ], - "@typescript-eslint/explicit-function-return-type": "warn", + "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. "no-empty": "warn" } } From acd0891356b98d40e394b3d7bf2de68bddb3e129 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2020 04:49:26 +0000 Subject: [PATCH 249/791] Prettify ADVANCED.md --- ADVANCED.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index fe11a994..9e31938a 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1361,32 +1361,32 @@ and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comme ```js module.exports = { - "env": { - "es6": true, - "node": true, - "jest": true + env: { + es6: true, + node: true, + jest: true }, - "extends": "eslint:recommended", - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - "parserOptions": { - "ecmaVersion": 2017, - "sourceType": "module" + extends: "eslint:recommended", + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + parserOptions: { + ecmaVersion: 2017, + sourceType: "module" }, - "rules": { - "indent": ["error", 2], + rules: { + indent: ["error", 2], "linebreak-style": ["error", "unix"], - "quotes": ["error", "single"], + quotes: ["error", "single"], "no-console": "warn", "no-unused-vars": "off", "@typescript-eslint/no-unused-vars": [ "error", - { "vars": "all", "args": "after-used", "ignoreRestSiblings": false } + { vars: "all", args: "after-used", ignoreRestSiblings: false } ], - "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. + "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. "no-empty": "warn" } -} +}; ``` Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. @@ -1436,7 +1436,7 @@ More `.eslintrc.json` options to consider with more options you may want for **a You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. -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. +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. ## Working with Non-TypeScript Libraries (writing your own index.d.ts) From e60c381b154f7f623e2cec5fe641e6afd5ddba7d Mon Sep 17 00:00:00 2001 From: JavaScript Joe Date: Sun, 5 Jan 2020 10:44:02 -0700 Subject: [PATCH 250/791] feat: move linting to README --- ADVANCED.md | 100 ---------------------------------------------- README.md | 111 ++++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 104 insertions(+), 107 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 9e31938a..7a1f2376 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1337,106 +1337,6 @@ Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can he - https://github.com/SamVerschueren/tsd - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) -## 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). - -> ⚠️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. - -Follow the TypeScript + ESLint docs at https://github.com/typescript-eslint/typescript-eslint: - -``` -yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint -``` - -add a `lint` script to your `package.json`: - -```json - "scripts": { - "lint": "eslint 'src/**/*.ts'" - }, -``` - -and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comments): - -```js -module.exports = { - env: { - es6: true, - node: true, - jest: true - }, - extends: "eslint:recommended", - parser: "@typescript-eslint/parser", - plugins: ["@typescript-eslint"], - parserOptions: { - ecmaVersion: 2017, - sourceType: "module" - }, - rules: { - indent: ["error", 2], - "linebreak-style": ["error", "unix"], - quotes: ["error", "single"], - "no-console": "warn", - "no-unused-vars": "off", - "@typescript-eslint/no-unused-vars": [ - "error", - { vars: "all", args: "after-used", ignoreRestSiblings: false } - ], - "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. - "no-empty": "warn" - } -}; -``` - -Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. - -More `.eslintrc.json` options to consider with more options you may want for **apps**: - -```json -{ - "extends": [ - "airbnb", - "prettier", - "prettier/react", - "plugin:prettier/recommended", - "plugin:jest/recommended", - "plugin:unicorn/recommended" - ], - "plugins": ["prettier", "jest", "unicorn"], - "parserOptions": { - "sourceType": "module", - "ecmaFeatures": { - "jsx": true - } - }, - "env": { - "es6": true, - "browser": true, - "jest": true - }, - "settings": { - "import/resolver": { - "node": { - "extensions": [".js", ".jsx", ".ts", ".tsx"] - } - } - }, - "overrides": [ - { - "files": ["**/*.ts", "**/*.tsx"], - "parser": "typescript-eslint-parser", - "rules": { - "no-undef": "off" - } - } - ] -} -``` - -You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. - -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. ## Working with Non-TypeScript Libraries (writing your own index.d.ts) diff --git a/README.md b/README.md index 2ebdec1a..835ccb8a 100644 --- a/README.md +++ b/README.md @@ -93,11 +93,11 @@ - [The Types I need Weren't Exported!](#the-types-i-need-werent-exported) - [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) - [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilties) -- [Troubleshooting Handbook: ESLint](#troubleshooting-handbook-eslint) - [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) - [Recommended React + TypeScript codebases to learn from](#recommended-react--typescript-codebases-to-learn-from) - [Recommended React + TypeScript talks](#recommended-react--typescript-talks) - [Editor Tooling and Integration](#editor-tooling-and-integration) +- [Linting](#linting) - [Other React + TypeScript resources](#other-react--typescript-resources) - [Time to Really Learn TypeScript](#time-to-really-learn-typescript) - [Example App](#example-app) @@ -1499,14 +1499,8 @@ these are all built in, [see source in es5.d.ts](https://github.com/microsoft/Ty - `Required`: Make all properties in an object required - `ReturnType` A function's return type -# Troubleshooting Handbook: ESLint - -**Note: TSLint is in maintenance and ESLint is the way forward for TypeScript. [You can convert TSlint to ESlint with this tool](https://github.com/typescript-eslint/tslint-to-eslint-config).** - This section needs writing, but you can probably find a good starting point with [Wes Bos' ESLint config](https://github.com/wesbos/eslint-config-wesbos) (which comes with a [YouTube intro](https://www.youtube.com/watch?v=lHAeK8t94as)). -Then, check out the [Prettier](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#prettier) and [Linting](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#linting) sections of the ADVANCED cheatsheet! - # 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/v2/en/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`): @@ -1654,6 +1648,109 @@ React Native Boilerplates: _contributed by [@spoeck](https://github.com/typescri - NeoVim: https://github.com/neoclide/coc.nvim - other discussion: https://mobile.twitter.com/ryanflorence/status/1085715595994095620 +# 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). + +> ⚠️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. + +Follow the TypeScript + ESLint docs at https://github.com/typescript-eslint/typescript-eslint: + +``` +yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint +``` + +add a `lint` script to your `package.json`: + +```json + "scripts": { + "lint": "eslint 'src/**/*.ts'" + }, +``` + +and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comments): + +```js +module.exports = { + env: { + es6: true, + node: true, + jest: true + }, + extends: "eslint:recommended", + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + parserOptions: { + ecmaVersion: 2017, + sourceType: "module" + }, + rules: { + indent: ["error", 2], + "linebreak-style": ["error", "unix"], + quotes: ["error", "single"], + "no-console": "warn", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { vars: "all", args: "after-used", ignoreRestSiblings: false } + ], + "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. + "no-empty": "warn" + } +}; +``` + +Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. + +More `.eslintrc.json` options to consider with more options you may want for **apps**: + +```json +{ + "extends": [ + "airbnb", + "prettier", + "prettier/react", + "plugin:prettier/recommended", + "plugin:jest/recommended", + "plugin:unicorn/recommended" + ], + "plugins": ["prettier", "jest", "unicorn"], + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "env": { + "es6": true, + "browser": true, + "jest": true + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + "overrides": [ + { + "files": ["**/*.ts", "**/*.tsx"], + "parser": "typescript-eslint-parser", + "rules": { + "no-undef": "off" + } + } + ] +} +``` + +You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. + +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. + +If you're looking for information on Prettier, check out the [Prettier](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#prettier). + # Other React + TypeScript resources - me! From eb77abea8ce945043820e230b584d7129b56c323 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Sun, 5 Jan 2020 17:44:17 +0000 Subject: [PATCH 251/791] Prettify ADVANCED.md --- ADVANCED.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 7a1f2376..99882484 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1337,7 +1337,6 @@ Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can he - https://github.com/SamVerschueren/tsd - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) - ## Working with Non-TypeScript Libraries (writing your own index.d.ts) Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: From 13df158a55149567d0aa60e19971094e2543ce10 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 8 Jan 2020 07:54:13 -0500 Subject: [PATCH 252/791] add netflix --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 3a12e80a..2d8a7c07 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -227,6 +227,7 @@ Old content that is possibly out of date - [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) - [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) ([podcast](https://softwareengineeringdaily.com/2017/08/11/typescript-at-slack-with-felix-rieseberg/)) +- [Netflix adoption story](https://www.youtube.com/watch?v=p5Hwb1YbNMY&feature=share) - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) - Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) From ff84ac6b0f4cba326bc794aced028e70aad7a4cd Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 8 Jan 2020 07:56:35 -0500 Subject: [PATCH 253/791] add dtslint --- ADVANCED.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ADVANCED.md b/ADVANCED.md index e47dc50d..70a43b07 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1336,6 +1336,7 @@ Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can he - https://github.com/azz/jest-runner-tsc - https://github.com/SamVerschueren/tsd - https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) +- https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)) ## Linting From 886f5964e8dc170be7457f8de700fd291ab7e859 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 8 Jan 2020 08:23:59 -0500 Subject: [PATCH 254/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 2d8a7c07..c0efab07 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -241,6 +241,7 @@ Open Source - [React Native CLI](https://github.com/react-native-community/cli/issues/683) - [Next.js](https://nextjs.org/blog/next-9) - [Redux](https://github.com/reduxjs/redux/pull/3536) +- [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) ## Links From 99287de937aa340d20b1ffdfef907494b875b1b7 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 13 Jan 2020 20:50:32 +0000 Subject: [PATCH 255/791] Format 39ddfa2 --- ADVANCED.md | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 0b3e9e5b..8ccc62a9 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1102,19 +1102,19 @@ let x = foo?.bar.baz(); // is equivalent to -let x = (foo === null || foo === undefined) ? undefined : foo.bar.baz(); +let x = foo === null || foo === undefined ? undefined : foo.bar.baz(); // Optional Element access function tryGetFirstElement(arr?: T[]) { - return arr?.[0]; + return arr?.[0]; } // Optional Call async function makeRequest(url: string, log?: (msg: string) => void) { - log?.(`Request started at ${new Date().toISOString()}`); - const result = (await fetch(url)).json(); - log?.(`Request finished at at ${new Date().toISOString()}`); - return result; + log?.(`Request started at ${new Date().toISOString()}`); + const result = (await fetch(url)).json(); + log?.(`Request finished at at ${new Date().toISOString()}`); + return result; } ``` @@ -1125,7 +1125,7 @@ let x = foo ?? bar(); // equivalent to -let x = (foo !== null && foo !== undefined) ? foo : bar(); +let x = foo !== null && foo !== undefined ? foo : bar(); ``` **YOU SHOULD USUALLY USE `??` WHEREVER YOU NORMALLY USE `||`** unless you truly mean falsiness: @@ -1141,17 +1141,17 @@ function ShowNumber({ value }: { value: number }) { ```tsx function assert(condition: any, msg?: string): asserts condition { - if (!condition) { - throw new AssertionError(msg) - } + if (!condition) { + throw new AssertionError(msg); + } } function yell(str) { - assert(typeof str === "string"); + assert(typeof str === "string"); - return str.toUppercase(); - // ~~~~~~~~~~~ - // error: Property 'toUppercase' does not exist on type 'string'. - // Did you mean 'toUpperCase'? + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? } ``` @@ -1159,19 +1159,19 @@ You can also assert without a custom function: ```tsx function assertIsString(val: any): asserts val is string { - if (typeof val !== "string") { - throw new AssertionError("Not a string!"); - } + if (typeof val !== "string") { + throw new AssertionError("Not a string!"); + } } function yell(str: any) { - assertIsString(str); + assertIsString(str); - // Now TypeScript knows that 'str' is a 'string'. + // Now TypeScript knows that 'str' is a 'string'. - return str.toUppercase(); - // ~~~~~~~~~~~ - // error: Property 'toUppercase' does not exist on type 'string'. - // Did you mean 'toUpperCase'? + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? } ``` From d945a24a8fd81e4c983cee7889f60ebee7d8e534 Mon Sep 17 00:00:00 2001 From: swyx Date: Wed, 15 Jan 2020 13:29:50 -0500 Subject: [PATCH 256/791] add new boilerplate --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 835ccb8a..2dd4af15 100644 --- a/README.md +++ b/README.md @@ -1624,6 +1624,7 @@ You can see examples of these included in the built in type declarations in the React Boilerplates: +- https://github.com/rwieruch/nextjs-firebase-authentication: Next.js + Firebase Starter: styled, tested, typed, and authenticated - [@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) - [webpack config tool](https://webpack.jakoblind.no/) is a visual tool for creating webpack projects with React and TypeScript - ready to go template with [Material-UI](https://material-ui.com/), routing and Redux From 5fd9d528b58a6e24ea20f2ffa65433cb383487ce Mon Sep 17 00:00:00 2001 From: Lauro Silva Date: Wed, 15 Jan 2020 14:57:33 -0800 Subject: [PATCH 257/791] Adding link to spanish repo --- ADVANCED.md | 1 + HOC.md | 1 + MIGRATING.md | 1 + 3 files changed, 3 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 8ccc62a9..dd1de394 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -17,6 +17,7 @@ [**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) diff --git a/HOC.md b/HOC.md index 35393c44..71c7ff08 100644 --- a/HOC.md +++ b/HOC.md @@ -17,6 +17,7 @@ [**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) diff --git a/MIGRATING.md b/MIGRATING.md index c0efab07..6e87513e 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -17,6 +17,7 @@ [**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | [**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | +[**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | [Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) From 974cf33d3e380eea4c8fbcbf1a5c1c7e8d952a1e Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Thu, 16 Jan 2020 03:22:12 +0000 Subject: [PATCH 258/791] Format 7de49a8 --- HOC.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/HOC.md b/HOC.md index 71c7ff08..4853365a 100644 --- a/HOC.md +++ b/HOC.md @@ -373,10 +373,7 @@ const commentActions = () => ({ addComment: (str: string) => comments.push({ text: str, id: comments.length }) }); -const ConnectedComment = connect( - commentSelector, - commentActions -)(CommentList); +const ConnectedComment = connect(commentSelector, commentActions)(CommentList); // these are the props to be injected by the HOC interface WithSubscriptionProps { From c9f90078be9b676879ee5b66328d2b27cf8af5ab Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 17 Jan 2020 10:23:52 -0500 Subject: [PATCH 259/791] add eslint + prettier gist --- ADVANCED.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index dd1de394..8fab09f9 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1328,7 +1328,9 @@ yarn add -D prettier husky lint-staged } ``` -This is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). +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) + +For library authors, this is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). ## Testing From aac4c6f9c79542c848d09a3e0b5049073b787614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dante=20Calder=C3=B3n?= Date: Tue, 21 Jan 2020 03:13:50 -0500 Subject: [PATCH 260/791] docs(advanced): Remove linting section from TOC (#179) --- ADVANCED.md | 1 - 1 file changed, 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index 8fab09f9..d43a67b8 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -82,7 +82,6 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Migrating from Flow](#migrating-from-flow) - [Prettier](#prettier) - [Testing](#testing) - - [Linting](#linting) - [Working with Non-TypeScript Libraries (writing your own index.d.ts)](#working-with-non-typescript-libraries-writing-your-own-indexdts) - [Section 4: @types/react and @types/react-dom APIs](#section-4-typesreact-and-typesreact-dom-apis) - [Adding non-standard attributes](#adding-non-standard-attributes) From 628c53eed1e46363a7f3aaa69184b7267dd75939 Mon Sep 17 00:00:00 2001 From: JustinR-FR Date: Thu, 23 Jan 2020 22:00:09 +0100 Subject: [PATCH 261/791] fix(178): edit link for docz propstable --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index d43a67b8..bb42fadd 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1211,7 +1211,7 @@ export class MyComponent extends React.Component { ## Commenting Components -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/documentation/components-api#propstable)). The main thing to remember is to use `/** YOUR_COMMENT_HERE */` syntax in the line just above whatever you're annotating. +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. ```tsx import React from "react"; From 39fe10459a9ab119010e083ad81bda71e5f28869 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 24 Jan 2020 09:43:08 -0500 Subject: [PATCH 262/791] Update MIGRATING.md --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 6e87513e..30eb816a 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -237,6 +237,7 @@ Open Source - [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) - [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) - [Google Workbox migration](https://github.com/GoogleChrome/workbox/pull/2058) +- [Chrome Dev Tools related issues](https://twitter.com/TimvdLippe/status/1220393069792694281) - [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) - [React Native CLI](https://github.com/react-native-community/cli/issues/683) From b638f6176dc596e63fc7597f494cbdfab343250e Mon Sep 17 00:00:00 2001 From: Denis Sokolov Date: Sat, 25 Jan 2020 15:36:06 +0200 Subject: [PATCH 263/791] Fix inverted condition in the /Link example --- ADVANCED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ADVANCED.md b/ADVANCED.md index bb42fadd..7489bc81 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -425,7 +425,7 @@ They don't even need to be completely different props, as long as they have at l type LinkProps = Omit & { to?: string }; function RouterLink(props: LinkProps | AnchorProps) { - if ("to" in props) { + if ("href" in props) { return ; } else { return ; From f620b3d8d16fd64b47089241d48405661921836b Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Sat, 25 Jan 2020 23:15:23 +0900 Subject: [PATCH 264/791] Fix TS Official Docs Link about Discriminated Unions Original url is missing `#discriminated-unions` hash fragment identifier end of string. So our browser can't reach **Discriminated Unions** section in the **Advanced Types** page. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2dd4af15..8e9425d7 100644 --- a/README.md +++ b/README.md @@ -1145,7 +1145,7 @@ function isAdmin(user: Admin | User): user is Admin { Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. -If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html) if you need help. (See also: [Basarat's writeup](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html)). This is handy in typing reducers for `useReducer` or Redux. +If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) if you need help. (See also: [Basarat's writeup](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html)). This is handy in typing reducers for `useReducer` or Redux. ## Optional Types From 32d6f46c400442a44132ac8624cf321ad7fc200e Mon Sep 17 00:00:00 2001 From: Shu Uesugi Date: Fri, 7 Feb 2020 08:52:15 -0800 Subject: [PATCH 265/791] Add chibicode's TS tutorials (#174) Co-authored-by: Lauro Silva <57044804+laurosilvacom@users.noreply.github.com> --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8e9425d7..9fd3eacb 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ ## Prerequisites 1. good understanding of [React](https://reactjs.org) -2. familiarity with [TypeScript Basic Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful) +2. familiarity with [TypeScript Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful. If you’re an absolute beginner in TypeScript, check out [chibicode’s tutorial](https://ts.chibicode.com/todo/).) 3. having read [the TypeScript section in the official React docs](https://reactjs.org/docs/static-type-checking.html#typescript). 4. having read [the React section of the new TypeScript playground](http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react) (optional: also step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section) @@ -1783,6 +1783,7 @@ It is worth mentioning some resources to help you get started: - 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.gitbooks.io/typescript/ - 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`) +- Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) # Example App From e78489444dbe11d68df015c7525de7ee16929a6f Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Tue, 11 Feb 2020 19:32:12 +0900 Subject: [PATCH 266/791] Update React TypeScript Todo Example 2019 -> 2020 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fd3eacb..6eb3523c 100644 --- a/README.md +++ b/README.md @@ -1787,7 +1787,7 @@ It is worth mentioning some resources to help you get started: # Example App -- [React TypeScript Todo Example 2019 Mid](https://github.com/ryota-murakami/react-typescript-todo-example-2019) +- [React TypeScript Todo Example 2020](https://github.com/laststance/react-typescript-todo-example-2020) # My question isn't answered here! From 859e0128f47ed1f6084b63482571a383bb329016 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 21 Feb 2020 11:53:42 -0500 Subject: [PATCH 267/791] add TS 3.8 --- ADVANCED.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/ADVANCED.md b/ADVANCED.md index 7489bc81..a0e1657c 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -74,6 +74,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [TypeScript 3.5](#typescript-35) - [TypeScript 3.6](#typescript-36) - [TypeScript 3.7](#typescript-37) + - [TypeScript 3.8](#typescript-38) - [Section 3: Misc. Concerns](#section-3-misc-concerns) - [Writing TypeScript Libraries instead of Apps](#writing-typescript-libraries-instead-of-apps) - [Commenting Components](#commenting-components) @@ -1179,6 +1180,50 @@ function yell(str: any) { You can now add `// @ts-nocheck` to the top of TypeScript files! good for migrations. +## TypeScript 3.8 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/)] + +1. Type-Only Imports and Exports + +```ts +import type { SomeThing } from "./some-module.js"; + +export type { SomeThing }; +``` + +2. ECMAScript Private Fields + +Not really React specific but ok Bloomberg + +3. `export * as ns` Syntax + +This is ES2020 syntax. Instead of + +```js +import * as utilities from "./utilities.js"; +export { utilities }; +``` + +you can do + +```js +export * as utilities from "./utilities.js"; +``` + +4. Top-Level `await` + +not React specific but gj Myles + +5. JSDoc Property Modifiers + +handy for JSDoc users - `@public, @private, @protected, @readonly` + +6. Better Directory Watching on Linux and watchOptions +7. “Fast and Loose” Incremental Checking + +`assumeChangesOnlyAffectDirectDependencies` reduces build times for extremely large codebases. + ## TypeScript Roadmap and Spec https://github.com/Microsoft/TypeScript/wiki/Roadmap From 47bd9f49fb850ff990ecfa4d1e6faa18cfea26c8 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Fri, 21 Feb 2020 16:53:48 +0000 Subject: [PATCH 268/791] Format 859e012 --- ADVANCED.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index a0e1657c..ad07260c 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -1198,14 +1198,14 @@ Not really React specific but ok Bloomberg 3. `export * as ns` Syntax -This is ES2020 syntax. Instead of +This is ES2020 syntax. Instead of ```js import * as utilities from "./utilities.js"; export { utilities }; ``` -you can do +you can do ```js export * as utilities from "./utilities.js"; From 7013c874c9016dc4db0ca27afca96ab401ecaafa Mon Sep 17 00:00:00 2001 From: Oren Zakay Date: Wed, 26 Feb 2020 15:47:34 +0200 Subject: [PATCH 269/791] fix: Incorrect displayName HOC.md (#189) * the HOC name is `withTheme` :) --- HOC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HOC.md b/HOC.md index 4853365a..cb2c42d8 100644 --- a/HOC.md +++ b/HOC.md @@ -109,7 +109,7 @@ export function withTheme( return class ComponentWithTheme extends React.Component< Optionalize > { - public static displayName = `withPages(${displayName})`; + public static displayName = `withTheme(${displayName})`; public render() { // Fetch the props you want inject. This could be done with context instead. From 9875ef58034ccc44536389e15b2a74b3718b4e29 Mon Sep 17 00:00:00 2001 From: swyx Date: Fri, 28 Feb 2020 20:34:00 -0500 Subject: [PATCH 270/791] add react router to MIGRATING --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 30eb816a..94a7bf19 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -242,6 +242,7 @@ Open Source - [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) - [React Native CLI](https://github.com/react-native-community/cli/issues/683) - [Next.js](https://nextjs.org/blog/next-9) +- [React Router](https://github.com/ReactTraining/react-router/issues/6955) - [Redux](https://github.com/reduxjs/redux/pull/3536) - [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) From 426b49ff0e358e75c5d8a6b4a4b2a8181a26c94d Mon Sep 17 00:00:00 2001 From: swyx Date: Tue, 3 Mar 2020 12:32:22 -0500 Subject: [PATCH 271/791] add polymorphic components links --- ADVANCED.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index ad07260c..998a4b59 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -53,7 +53,7 @@ The best tool for creating React + TS libraries right now is [`tsdx`](https://gi - [Higher Order Components](#higher-order-components-hocs) - [Render Props](#render-props) - [Conditionally Rendering Components](#conditinonally-rendering-components) - - [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered) + - [Polymorphic Components (passing a component to be rendered, e.g. with `as` props)](#polymorphic-components-passing-a-component-to-be-rendered-e-g-with-as-props) - [Generic Components](#generic-components) - [Typing a Component that Accepts Different Props](#typing-a-component-that-accepts-different-props) - [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both) @@ -179,7 +179,7 @@ function App() { [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) -## `as` props (passing a component to be rendered) +## Polymorphic Components (passing a component to be rendered, e.g. with `as` props) `ElementType` is pretty useful to cover most types that can be passed to createElement e.g. @@ -191,6 +191,11 @@ function PassThrough(props: { as: React.ElementType }) { } ``` +For more info you can refer to these resources: + +- https://blog.andrewbran.ch/polymorphic-react-components/ +- https://github.com/kripod/react-polymorphic-box + [Thanks @eps1lon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69) for this ## Generic Components From 1c1f76ff928d3121c4419aa986a96673afa40fee Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 5 Mar 2020 18:09:59 -0800 Subject: [PATCH 272/791] context-motivation --- README.md | 87 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 81 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6eb3523c..0823511b 100644 --- a/README.md +++ b/README.md @@ -870,19 +870,94 @@ Of course, if you're making any sort of significant form, [you should use Formik ## Context -Using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with **no `defaultValue`, yet no need to check for `undefined`**: +You can use the `useContext` API in mostly the same way you would in JavaScript, but it takes a bit of boilerplate out of the box under TypeScript's `strictNullChecks` mode. Here's the most basic example: + +```ts +import * as React from "react"; + +const currentUserContext = React.createContext(undefined); + +function EnthusasticGreeting() { + const currentUser = React.useContext(currentUserContext); + return
    HELLO {currentUser!.toUpperCase()}!
    ; +} + +function App() { + return + + ; +} +``` + +Notice the explicit type arguments which we need because we don't have a default `string` value: + +```ts +const currentUserContext = React.createContext(undefined); +// ^^^^^^^^^^^^^^^^^^ +``` + +along with the non-null assertion to tell TypeScript that `currentUser` is definitely going to be there: + +```ts + return
    HELLO {currentUser!.toUpperCase()}!
    ; +// ^ +``` + +This is unfortunate because *we know* that later in our app, a `Provider` is going to fill in the context. + +We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: + +```tsx +import * as React from "react"; + +/** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ +function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple +} + +// Usage: + +// We still have to specify a type, but no default! +export const [useCurrentUserName, CurrentUserProvider] = createCtx(); + +function EnthusasticGreeting() { + const currentUser = useCurrentUserName(); + return
    HELLO {currentUser.toUpperCase()}!
    ; +} + +function App() { + return + + ; +} +``` + +[View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) + +You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). ```tsx -// create context with no upfront defaultValue -// without having to do undefined check all the time -function createCtx
    () { +/** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ +function createCtx() { const ctx = React.createContext(undefined); function useCtx() { const c = React.useContext(ctx); - if (!c) throw new Error("useCtx must be inside a Provider with a value"); + if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); return c; } - return [useCtx, ctx.Provider] as const; // make TypeScript infer a tuple, not an array of union types + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple } // usage From 634ea53756a646fc9bbb18e46498b5c15fd134ad Mon Sep 17 00:00:00 2001 From: swyx Date: Sun, 8 Mar 2020 21:05:35 -0400 Subject: [PATCH 273/791] add migration of history package to MIGRATING. MD --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 94a7bf19..fbb0b94f 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -243,6 +243,7 @@ Open Source - [React Native CLI](https://github.com/react-native-community/cli/issues/683) - [Next.js](https://nextjs.org/blog/next-9) - [React Router](https://github.com/ReactTraining/react-router/issues/6955) + - [history] (https://github.com/ReactTraining/history/pull/774) - [Redux](https://github.com/reduxjs/redux/pull/3536) - [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) From c64d0e3685cd0e10787fb44014912fbc9773d5b1 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Mon, 9 Mar 2020 01:05:40 +0000 Subject: [PATCH 274/791] Format 634ea53 --- MIGRATING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MIGRATING.md b/MIGRATING.md index fbb0b94f..16faf7be 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -243,7 +243,7 @@ Open Source - [React Native CLI](https://github.com/react-native-community/cli/issues/683) - [Next.js](https://nextjs.org/blog/next-9) - [React Router](https://github.com/ReactTraining/react-router/issues/6955) - - [history] (https://github.com/ReactTraining/history/pull/774) + - [history](https://github.com/ReactTraining/history/pull/774) - [Redux](https://github.com/reduxjs/redux/pull/3536) - [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) From 223a2c02daea5b04d84ca12eb8c1c3d208a40440 Mon Sep 17 00:00:00 2001 From: swyx Date: Sun, 8 Mar 2020 21:28:17 -0400 Subject: [PATCH 275/791] strict null checks blog post --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index 16faf7be..f3c8cfff 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -192,6 +192,7 @@ Gradually add [more `strict` mode flags](https://www.typescriptlang.org/docs/han - [Hootsuite][hootsuite] - [Storybook's migration (PR)](https://github.com/storybooks/storybook/issues/5030) - [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 +- incrementally adding strict null checks https://code.visualstudio.com/blogs/2019/05/23/strict-null Old content that is possibly out of date From 9b72bf4ba10945527cf69ee91e401306446fae9d Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 12 Mar 2020 15:26:21 -0400 Subject: [PATCH 276/791] add Heap's migration story --- MIGRATING.md | 1 + 1 file changed, 1 insertion(+) diff --git a/MIGRATING.md b/MIGRATING.md index f3c8cfff..f7db99a5 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -232,6 +232,7 @@ Old content that is possibly out of date - [Netflix adoption story](https://www.youtube.com/watch?v=p5Hwb1YbNMY&feature=share) - [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) - Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) +- [Heap - How we failed, then succeeded, at migrating to TypeScript](https://heap.io/blog/analysis/migrating-to-typescript) Open Source From 884f20355eb830e1d03303786a140c1862ab0e1d Mon Sep 17 00:00:00 2001 From: Ryota Murakami Date: Sun, 15 Mar 2020 23:24:23 +0900 Subject: [PATCH 277/791] docs(readme): Update Example App name (#197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ´ --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6eb3523c..c0555131 100644 --- a/README.md +++ b/README.md @@ -1787,7 +1787,7 @@ It is worth mentioning some resources to help you get started: # Example App -- [React TypeScript Todo Example 2020](https://github.com/laststance/react-typescript-todo-example-2020) +- [Create React App TypeScript Todo Example 2020](https://github.com/laststance/create-react-app-typescript-todo-example-2020) # My question isn't answered here! From d64f57aa306ba7df711af72617560644814d367c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Szab=C3=B3?= Date: Tue, 17 Mar 2020 10:54:16 +0100 Subject: [PATCH 278/791] docs(readme): Fix type branding link (#198) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c0555131..c44449ad 100644 --- a/README.md +++ b/README.md @@ -1227,7 +1227,7 @@ Of course, try to actually handle the null case instead of asserting :) ## Simulating Nominal Types -TS' structural typing is handy, until it is inconvenient. However you can simulate nominal typing with [`type branding`](https://basarat.gitbooks.io/typescript/docs/tips/nominalTyping.html): +TS' structural typing is handy, until it is inconvenient. However you can simulate nominal typing with [`type branding`](https://basarat.gitbook.io/typescript/main-1/nominaltyping): ```ts type OrderID = string & { readonly brand: unique symbol }; From 019ec5bd1b6faed3cbb8bac99bbc210f1489c6b8 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Fri, 20 Mar 2020 16:51:21 +0100 Subject: [PATCH 279/791] fix(advanced): Fix syntax errors revealed by prettier (#201) * fix(advanced): Fix syntax errors revealed by prettier * bump prettier --- ADVANCED.md | 112 ++++++++++++++++++++++++++++----------------------- package.json | 2 +- yarn.lock | 8 ++-- 3 files changed, 66 insertions(+), 56 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 998a4b59..e4bb4194 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -474,31 +474,35 @@ const Link = ( If you want to conditionally render a component, sometimes is better to use [React's composition model](https://reactjs.org/docs/composition-vs-inheritance.html) to have simpler components and better to understand typings: ```tsx -type AnchorProps = React.AnchorHTMLAttributes -type RouterLinkProps = Omit +type AnchorProps = React.AnchorHTMLAttributes; +type RouterLinkProps = Omit; interface Button { - as: React.ComponentClass | 'a' + as: React.ComponentClass | "a"; } -const Button: React.FunctionComponent
    @@ -816,41 +820,41 @@ You can also describe exceptions with special-purpose data types (don't say mona ```ts interface Option { - flatMap(f: (value: T) => None): None - flatMap(f: (value: T) => Option): Option - getOrElse(value: T): T + flatMap(f: (value: T) => None): None; + flatMap(f: (value: T) => Option): FormikOption; + getOrElse(value: T): T; } class Some implements Option { constructor(private value: T) {} - flatMap(f: (value: T) => None): None - flatMap(f: (value: T) => Some): Some + flatMap(f: (value: T) => None): None; + flatMap(f: (value: T) => Some): Some; flatMap(f: (value: T) => Option): Option { - return f(this.value) + return f(this.value); } getOrElse(): T { - return this.value + return this.value; } } class None implements Option { flatMap(): None { - return this + return this; } getOrElse(value: U): U { - return value + return value; } } // now you can use it like: let result = Option(6) // Some - .flatMap(n => Option(n*3)) // Some - .flatMap(n = new None) // None - .getOrElse(7) + .flatMap(n => Option(n * 3)) // Some + .flatMap((n = new None())) // None + .getOrElse(7); // or: let result = ask() // Option - .flatMap(parse) // Option - .flatMap(d => new Some(d.toISOString()) // Option - .getOrElse('error parsing string') + .flatMap(parse) // Option + .flatMap(d => new Some(d.toISOString())) // Option + .getOrElse("error parsing string"); ``` ## Third Party Libraries @@ -889,11 +893,19 @@ Helps with typing/using generic components: ```tsx // instead of -) => ....}/> +) => { + /* your code here ... */ + }} +/>; // usage - render={props => ...}/> - data={12} /> + + render={props => { + /* your code here ... */ + }} +/>; + data={12} />; ``` More info: https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components @@ -966,6 +978,8 @@ if (typeof response === "string") { } ``` +TODO: blame this change. Don't know what this shouldve done + You can also assert a type, or use a **type guard** against an `unknown` type. This is better than resorting to `any`. 4. Project References @@ -974,7 +988,7 @@ Project references allow TypeScript projects to depend on other TypeScript proje In each folder, create a tsconfig.json that includes at least: -```js +```json { "compilerOptions": { "composite": true, // tells TSC it is a subproject of a larger project @@ -982,10 +996,9 @@ In each folder, create a tsconfig.json that includes at least: "declarationMap": true, // sourcemaps for .d.ts "rootDir": "." // specify compile it relative to root project at . }, - "include": [ - "./**/*.ts - ], - "references": [ // (optional) array of subprojects your subproject depends on + "include": ["./**/*.ts"], + "references": [ + // (optional) array of subprojects your subproject depends on { "path": "../myreferencedproject", // must have tsconfig.json "prepend": true // concatenate js and sourcemaps generated by this subproject, if and only if using outFile @@ -996,13 +1009,10 @@ In each folder, create a tsconfig.json that includes at least: and the root `tsconfig.json` that references top level subproject: -```js +```json { - "files: [], - "references": [ - {"path": "./proj1"}, - {"path": "./proj2"}, - ] + "files": [], + "references": [{ "path": "./proj1" }, { "path": "./proj2" }] } ``` @@ -1010,9 +1020,9 @@ and you must run `tsc --build` or `tsc -b`. To save the tsconfig boilerplate, you can use the `extends` option: -```js +```json { - "extends": "../tsconfig.base", + "extends": "../tsconfig.base" // more stuff here } ``` @@ -1346,9 +1356,11 @@ If you have specific advice in this area, please file a PR! There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! -```js -yarn add -D prettier husky lint-staged +```bash +$ yarn add -D prettier husky lint-staged +``` +```json // inside package.json { //... @@ -1363,9 +1375,7 @@ yarn add -D prettier husky lint-staged "prettier --trailing-comma es5 --single-quote --write", "git add" ], - "ignore": [ - "**/dist/*, **/node_modules/*" - ] + "ignore": ["**/dist/*, **/node_modules/*"] } }, "prettier": { diff --git a/package.json b/package.json index 5016b1ad..d28e99de 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,6 @@ }, "homepage": "https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#readme", "devDependencies": { - "prettier": "^1.17.0" + "prettier": "^1.19.1" } } diff --git a/yarn.lock b/yarn.lock index 0619ba57..6fc84143 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -prettier@^1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.17.0.tgz#53b303676eed22cc14a9f0cec09b477b3026c008" - integrity sha512-sXe5lSt2WQlCbydGETgfm1YBShgOX4HxQkFPvbxkcwgDvGDeqVau8h+12+lmSVlP3rHPz0oavfddSZg/q+Szjw== +prettier@^1.19.1: + version "1.19.1" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" + integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== From df4642a5c866f375e35e835d60982223d0bc6584 Mon Sep 17 00:00:00 2001 From: Noskov Artem Date: Fri, 20 Mar 2020 21:01:16 +0500 Subject: [PATCH 280/791] fix(advanced): Use correct comment syntax (#200) * Fix typo in ADVANCED.md * Fix prettier warning * fix(advanced): Use correct comment syntax Co-authored-by: Sebastian Silbermann --- ADVANCED.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index e4bb4194..cf29bed3 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -228,8 +228,8 @@ ReactDOM.render( items={["a", "b"]} // type of 'string' inferred renderItem={item => (
  • - {item.toPrecision(3)} // Error: Property 'toPrecision' does not exist on - type 'string'. + {/* Error: Property 'toPrecision' does not exist on type 'string'. */} + {item.toPrecision(3)}
  • )} />, From b153551c96ea099e0b0384735d0e35bb23ac76f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20Je=20Garc=C3=ADa?= Date: Tue, 24 Mar 2020 12:44:26 -0600 Subject: [PATCH 281/791] docs: Update useful React Prop Types (#204) I believe this sentence was suppose to say `strings` instead of `functions`. Functions are not valid React children unless you explicitly mean so for a children function. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c44449ad..68ebfc90 100644 --- a/README.md +++ b/README.md @@ -679,7 +679,7 @@ Notice we have used the TSDoc `/** comment */` style here on each prop. You can ```tsx export declare interface AppProps { children1: JSX.Element; // bad, doesnt account for arrays - children2: JSX.Element | JSX.Element[]; // meh, doesnt accept functions + children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility children4: React.ReactChild[]; // better children: React.ReactNode; // best, accepts everything From dbbd8020c7e4d52a614ac2fd9f7c851b8b542ab4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A1vid=20Szab=C3=B3?= Date: Tue, 24 Mar 2020 19:46:05 +0100 Subject: [PATCH 282/791] docs: Fix basarat links (#203) --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 68ebfc90..35bcf1d1 100644 --- a/README.md +++ b/README.md @@ -1145,7 +1145,7 @@ function isAdmin(user: Admin | User): user is Admin { Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. -If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) if you need help. (See also: [Basarat's writeup](https://basarat.gitbooks.io/typescript/docs/types/discriminated-unions.html)). This is handy in typing reducers for `useReducer` or Redux. +If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) if you need help. (See also: [Basarat's writeup](https://basarat.gitbook.io/typescript/type-system/discriminated-unions)). This is handy in typing reducers for `useReducer` or Redux. ## Optional Types @@ -1757,7 +1757,7 @@ If you're looking for information on Prettier, check out the [Prettier](https:// - me! - - **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**. - [Ultimate React Component Patterns with TypeScript 2.8](https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935) -- [Basarat's TypeScript gitbook has a React section](https://basarat.gitbooks.io/typescript/content/docs/jsx/react.html) with an [Egghead.io course](https://egghead.io/courses/use-typescript-to-develop-react-applications) as well. +- [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. - [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) - [Sindre Sorhus' TypeScript Style Guide](https://github.com/sindresorhus/typescript-definition-style-guide) - [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 doesnt seem to be frequently updated anymore. @@ -1781,7 +1781,7 @@ It is worth mentioning some resources to help you get started: - Step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section, written by @Orta - 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.gitbooks.io/typescript/ +- Basarat's Deep Dive: https://basarat.gitbook.io/typescript/ - 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`) - Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) From 1ab7dd2c33055eace383cece63a67e95ab08dfc1 Mon Sep 17 00:00:00 2001 From: Aziz Khambati Date: Wed, 25 Mar 2020 02:24:47 +0530 Subject: [PATCH 283/791] docs(advanced) Correct the section about discrinated types (#205) --- ADVANCED.md | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index cf29bed3..46c5fa37 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -511,23 +511,6 @@ You may also want to use Discriminated Unions, please check out [Expressive Reac Here is a brief intuition for **Discriminated Union Types**: -```ts -type UserTextEvent = { value: string; target: HTMLInputElement }; -type UserMouseEvent = { value: [number, number]; target: HTMLElement }; -type UserEvent = UserTextEvent | UserMouseEvent; -function handle(event: UserEvent) { - if (typeof event.value === "string") { - event.value; // string - event.target; // HTMLInputElement | HTMLElement (!!!!) - return; - } - event.value; // [number, number] - event.target; // HTMLInputElement | HTMLElement (!!!!) -} -``` - -Even though we have narrowed based on `event.value`, the logic doesn't filter up and sideways to `event.target`. This is because a union type `UserTextEvent | UserMouseEvent` could be BOTH at once. So TypeScript needs a better hint. The solution is to use a literal type to tag each case of your union type: - ```ts type UserTextEvent = { type: "TextEvent"; @@ -551,6 +534,30 @@ function handle(event: UserEvent) { } ``` +
    + + Take care: TypeScript does not narrow the type of a Discriminated Union on the basis of typeof checks. The type guard has to be on the value of a key and not it's type. + + +```ts +type UserTextEvent = { value: string; target: HTMLInputElement }; +type UserMouseEvent = { value: [number, number]; target: HTMLElement }; +type UserEvent = UserTextEvent | UserMouseEvent; +function handle(event: UserEvent) { + if (typeof event.value === "string") { + event.value; // string + event.target; // HTMLInputElement | HTMLElement (!!!!) + return; + } + event.value; // [number, number] + event.target; // HTMLInputElement | HTMLElement (!!!!) +} +``` + +The above example does not work as we are not checking the value of `event.value` but only it's type. Read more about it [microsoft/TypeScript#30506 (comment)](https://github.com/microsoft/TypeScript/issues/30506#issuecomment-474858198) + +
    + To streamline this you may also combine this with the concept of **User-Defined Type Guards**: ```ts From af5fcd0d1ae4c44adaa788c75c27ffedf472b9fe Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 26 Mar 2020 06:43:53 +0800 Subject: [PATCH 284/791] address React.FC and React.FunctionComponent equality closes https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/190 --- README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 35bcf1d1..1d516052 100644 --- a/README.md +++ b/README.md @@ -165,17 +165,20 @@ const App = ({ message }: AppProps) =>
    {message}
    ; What about `React.FC`/`React.FunctionComponent`? -You can also write components with `React.FunctionComponent` (or the shorthand `React.FC`): +You can also write components with `React.FunctionComponent` (or the shorthand `React.FC` - they are the same): ```tsx -const App: React.FC<{ message: string }> = ({ message }) => ( +const App: React.FunctionComponent<{ message: string }> = ({ message }) => (
    {message}
    ); ``` Some differences from the "normal function" version: -- It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps` - **However**, there are currently known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87) - scroll down to our `defaultProps` section for typing recommendations there. +- `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). + +- It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps`. + - Note that there are some known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). We maintain a separate `defaultProps` section you can also look up. - It provides an implicit definition of `children` (see below) - however there are some issues with the implicit `children` type (e.g. [DefinitelyTyped#33006](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006)), and it might considered better style to be explicit about components that consume `children`, anyway. @@ -188,9 +191,7 @@ const Title: React.FunctionComponent<{ title: string }> = ({ - _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. -- `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). - -In most cases it makes very little difference which syntax is used, but the `React.FC` syntax is slightly more verbose without providing clear advantage, so precedence was given to the "normal function" syntax. +In most cases it makes very little difference which syntax is used, but you may prefer the more explicit nature of `React.FunctionComponent`. From 2fc481062662153b146d752caf0f62321245da6c Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2020 22:44:00 +0000 Subject: [PATCH 285/791] Format af5fcd0 --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1d516052..300a8128 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,7 @@ Some differences from the "normal function" version: - `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). - It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps`. + - Note that there are some known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). We maintain a separate `defaultProps` section you can also look up. - It provides an implicit definition of `children` (see below) - however there are some issues with the implicit `children` type (e.g. [DefinitelyTyped#33006](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006)), and it might considered better style to be explicit about components that consume `children`, anyway. From 30337af3dcb35c173ca37f6dbbe6b27ee1b7be61 Mon Sep 17 00:00:00 2001 From: swyx Date: Thu, 26 Mar 2020 11:44:12 +0800 Subject: [PATCH 286/791] add non undefined assertion example for createcontext --- README.md | 214 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 112 insertions(+), 102 deletions(-) diff --git a/README.md b/README.md index 0823511b..e866015b 100644 --- a/README.md +++ b/README.md @@ -883,7 +883,7 @@ function EnthusasticGreeting() { } function App() { - return + return ; } @@ -905,122 +905,132 @@ along with the non-null assertion to tell TypeScript that `currentUser` is defin This is unfortunate because *we know* that later in our app, a `Provider` is going to fill in the context. -We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: +There are a few solutions for this: -```tsx -import * as React from "react"; - -/** - * A helper to create a Context and Provider with no upfront default value, and - * without having to check for undefined all the time. - */ -function createCtx
    () { - const ctx = React.createContext(undefined); - function useCtx() { - const c = React.useContext(ctx); - if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); - return c; - } - return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple -} - -// Usage: +1. You can get around this by asserting non null: -// We still have to specify a type, but no default! -export const [useCurrentUserName, CurrentUserProvider] = createCtx(); + ```ts + const currentUserContext = React.createContext(undefined!); + ``` + + ([Playground here](https://www.typescriptlang.org/play/index.html?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUNBhgXABxSixgwxNHOABvOjg4JlZ2Lh5+QSg4WWw8RQCsdXEpE05uLF4BIWLNZ0S4ShguZjgtC2AANyMACS8AGX6AeXjyjOqoBRgIPjAwGrQsGIBfey0Aeg7u+mW6V2Z3TwBBOZj4hqaWtrHKzJqxTQUABWJO4CtszuQAGw4saTIAGVfMgAO7MMhGBpJLQ+GD+QJsELhLCRfQGODrKEw9Y3KpZWpSZ6vd5CIw7IA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error. -function EnthusasticGreeting() { - const currentUser = useCurrentUserName(); - return
    HELLO {currentUser.toUpperCase()}!
    ; -} +2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: -function App() { - return - - ; -} -``` + ```tsx + import * as React from "react"; -[View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) - -You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } -```tsx -/** - * A helper to create a Context and Provider with no upfront default value, and - * without having to check for undefined all the time. - */ -function createCtx() { - const ctx = React.createContext(undefined); - function useCtx() { - const c = React.useContext(ctx); - if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); - return c; - } - return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple -} + // Usage: -// usage + // We still have to specify a type, but no default! + export const [useCurrentUserName, CurrentUserProvider] = createCtx(); -export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! -export function App() { - const key = useCustomHook("key"); // get a value from a hook, must be in a component - return ( - - - - ); -} -export function Component() { - const key = useCtx(); // can still use without null check! - return
    {key}
    ; -} -``` + function EnthusasticGreeting() { + const currentUser = useCurrentUserName(); + return
    HELLO {currentUser.toUpperCase()}!
    ; + } -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) + function App() { + return + + ; + } + ``` + + [View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) + +3. You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). + + ```tsx + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } -Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: + // usage -```tsx -export function createCtx(defaultValue: A) { - type UpdateType = React.Dispatch>; - const defaultUpdate: UpdateType = () => defaultValue; - const ctx = React.createContext({ - state: defaultValue, - update: defaultUpdate - }); - function Provider(props: React.PropsWithChildren<{}>) { - const [state, update] = React.useState(defaultValue); - return ; - } - return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] -} + export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! + export function App() { + const key = useCustomHook("key"); // get a value from a hook, must be in a component + return ( + + + + ); + } + export function Component() { + const key = useCtx(); // can still use without null check! + return
    {key}
    ; + } + ``` + + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) + +4. Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: + + ```tsx + export function createCtx
    (defaultValue: A) { + type UpdateType = React.Dispatch>; + const defaultUpdate: UpdateType = () => defaultValue; + const ctx = React.createContext({ + state: defaultValue, + update: defaultUpdate + }); + function Provider(props: React.PropsWithChildren<{}>) { + const [state, update] = React.useState(defaultValue); + return ; + } + return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] + } -// usage + // usage -const [ctx, TextProvider] = createCtx("someText"); -export const TextContext = ctx; -export function App() { - return ( - - - - ); -} -export function Component() { - const { state, update } = React.useContext(TextContext); - return ( - - ); -} -``` + const [ctx, TextProvider] = createCtx("someText"); + export const TextContext = ctx; + export function App() { + return ( + + + + ); + } + export function Component() { + const { state, update } = React.useContext(TextContext); + return ( + + ); + } + ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) -A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. +5. A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful.
    From a2180e2a44fdef9fde4ad7c8b2595dd478e66161 Mon Sep 17 00:00:00 2001 From: "prettifier[bot]" <45367598+prettifier[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2020 03:44:48 +0000 Subject: [PATCH 287/791] Format fc32b2a --- README.md | 240 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 124 insertions(+), 116 deletions(-) diff --git a/README.md b/README.md index fd1f6b59..41f88b2e 100644 --- a/README.md +++ b/README.md @@ -885,9 +885,11 @@ function EnthusasticGreeting() { } function App() { - return - - ; + return ( + + + + ); } ``` @@ -901,136 +903,142 @@ const currentUserContext = React.createContext(undefined); along with the non-null assertion to tell TypeScript that `currentUser` is definitely going to be there: ```ts - return
    HELLO {currentUser!.toUpperCase()}!
    ; +return
    HELLO {currentUser!.toUpperCase()}!
    ; // ^ ``` -This is unfortunate because *we know* that later in our app, a `Provider` is going to fill in the context. +This is unfortunate because _we know_ that later in our app, a `Provider` is going to fill in the context. There are a few solutions for this: 1. You can get around this by asserting non null: - ```ts - const currentUserContext = React.createContext(undefined!); - ``` - - ([Playground here](https://www.typescriptlang.org/play/index.html?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUNBhgXABxSixgwxNHOABvOjg4JlZ2Lh5+QSg4WWw8RQCsdXEpE05uLF4BIWLNZ0S4ShguZjgtC2AANyMACS8AGX6AeXjyjOqoBRgIPjAwGrQsGIBfey0Aeg7u+mW6V2Z3TwBBOZj4hqaWtrHKzJqxTQUABWJO4CtszuQAGw4saTIAGVfMgAO7MMhGBpJLQ+GD+QJsELhLCRfQGODrKEw9Y3KpZWpSZ6vd5CIw7IA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error. - -2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: - - ```tsx - import * as React from "react"; - - /** - * A helper to create a Context and Provider with no upfront default value, and - * without having to check for undefined all the time. - */ - function createCtx
    () { - const ctx = React.createContext(undefined); - function useCtx() { - const c = React.useContext(ctx); - if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); - return c; - } - return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple - } - - // Usage: + ```ts + const currentUserContext = React.createContext(undefined!); + ``` - // We still have to specify a type, but no default! - export const [useCurrentUserName, CurrentUserProvider] = createCtx(); - - function EnthusasticGreeting() { - const currentUser = useCurrentUserName(); - return
    HELLO {currentUser.toUpperCase()}!
    ; - } + ([Playground here](https://www.typescriptlang.org/play/index.html?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUNBhgXABxSixgwxNHOABvOjg4JlZ2Lh5+QSg4WWw8RQCsdXEpE05uLF4BIWLNZ0S4ShguZjgtC2AANyMACS8AGX6AeXjyjOqoBRgIPjAwGrQsGIBfey0Aeg7u+mW6V2Z3TwBBOZj4hqaWtrHKzJqxTQUABWJO4CtszuQAGw4saTIAGVfMgAO7MMhGBpJLQ+GD+QJsELhLCRfQGODrKEw9Y3KpZWpSZ6vd5CIw7IA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error. - function App() { - return - - ; - } - ``` +2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: - [View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) + ```tsx + import * as React from "react"; + + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) + throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } + + // Usage: + + // We still have to specify a type, but no default! + export const [useCurrentUserName, CurrentUserProvider] = createCtx(); + + function EnthusasticGreeting() { + const currentUser = useCurrentUserName(); + return
    HELLO {currentUser.toUpperCase()}!
    ; + } + + function App() { + return ( + + + + ); + } + ``` + + [View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) 3. You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). - ```tsx - /** - * A helper to create a Context and Provider with no upfront default value, and - * without having to check for undefined all the time. - */ - function createCtx
    () { - const ctx = React.createContext(undefined); - function useCtx() { - const c = React.useContext(ctx); - if (c === undefined) throw new Error("useCtx must be inside a Provider with a value"); - return c; - } - return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple - } - - // usage - - export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! - export function App() { - const key = useCustomHook("key"); // get a value from a hook, must be in a component - return ( - - - - ); - } - export function Component() { - const key = useCtx(); // can still use without null check! - return
    {key}
    ; - } - ``` - - [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) + ```tsx + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) + throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } + + // usage + + export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! + export function App() { + const key = useCustomHook("key"); // get a value from a hook, must be in a component + return ( + + + + ); + } + export function Component() { + const key = useCtx(); // can still use without null check! + return
    {key}
    ; + } + ``` + + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) 4. Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: - ```tsx - export function createCtx
    (defaultValue: A) { - type UpdateType = React.Dispatch>; - const defaultUpdate: UpdateType = () => defaultValue; - const ctx = React.createContext({ - state: defaultValue, - update: defaultUpdate - }); - function Provider(props: React.PropsWithChildren<{}>) { - const [state, update] = React.useState(defaultValue); - return ; - } - return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] - } - - // usage - - const [ctx, TextProvider] = createCtx("someText"); - export const TextContext = ctx; - export function App() { - return ( - - - - ); - } - export function Component() { - const { state, update } = React.useContext(TextContext); - return ( - - ); - } - ``` - - [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) + ```tsx + export function createCtx(defaultValue: A) { + type UpdateType = React.Dispatch< + React.SetStateAction + >; + const defaultUpdate: UpdateType = () => defaultValue; + const ctx = React.createContext({ + state: defaultValue, + update: defaultUpdate + }); + function Provider(props: React.PropsWithChildren<{}>) { + const [state, update] = React.useState(defaultValue); + return ; + } + return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] + } + + // usage + + const [ctx, TextProvider] = createCtx("someText"); + export const TextContext = ctx; + export function App() { + return ( + + + + ); + } + export function Component() { + const { state, update } = React.useContext(TextContext); + return ( + + ); + } + ``` + + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) 5. A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. From d3e954005b26229e80295cbe183a827ddb101889 Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Thu, 26 Mar 2020 09:31:38 +0100 Subject: [PATCH 288/791] chore: check format with actions (#209) --- .github/workflows/main.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/main.yml diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 00000000..20565789 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,21 @@ +name: CI +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + prettier: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Format code + run: yarn format + + - name: Format correct? + run: git diff --exit-code From f34640133cb5b570d163d7e00ed258253a86c8ad Mon Sep 17 00:00:00 2001 From: Sebastian Silbermann Date: Mon, 30 Mar 2020 09:19:17 +0200 Subject: [PATCH 289/791] chore: Bump prettier to 2.0 (#208) --- ADVANCED.md | 24 ++++++++--------- HOC.md | 29 ++++++++++---------- README.md | 74 ++++++++++++++++++++++++++-------------------------- package.json | 2 +- yarn.lock | 8 +++--- 5 files changed, 69 insertions(+), 68 deletions(-) diff --git a/ADVANCED.md b/ADVANCED.md index 46c5fa37..c350f317 100644 --- a/ADVANCED.md +++ b/ADVANCED.md @@ -226,7 +226,7 @@ You can then use the generic components and get nice type safety through type in ReactDOM.render( ( + renderItem={(item) => (
  • {/* Error: Property 'toPrecision' does not exist on type 'string'. */} {item.toPrecision(3)} @@ -243,7 +243,7 @@ As of [TS 2.9](#typescript-29), you can also supply the type parameter in your J ReactDOM.render( items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'. - renderItem={item =>
  • {item.toPrecision(3)}
  • } + renderItem={(item) =>
  • {item.toPrecision(3)}
  • } />, document.body ); @@ -288,7 +288,7 @@ interface State { class List extends React.PureComponent, State> { // You can use type T inside List class. state: Readonly> = { - items: [] + items: [], }; render() { const { items, renderItem } = this.props; @@ -352,7 +352,7 @@ Type '{ children: string; item: string; renderItem: (item: string) => string; }' */ const wrapper = ( - item}> + item}> {test} ); @@ -481,7 +481,7 @@ interface Button { as: React.ComponentClass | "a"; } -const Button: React.FunctionComgetOrElseponent
    - {/* 😭 Error, `disabled` doesnt exist on anchor element */} - - - ); -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) - -## Polymorphic Components (passing a component to be rendered, e.g. with `as` props) - -`ElementType` is pretty useful to cover most types that can be passed to createElement e.g. - -```tsx -function PassThrough(props: { as: React.ElementType }) { - const { as: Component } = props; - - return ; -} -``` - -For more info you can refer to these resources: - -- https://blog.andrewbran.ch/polymorphic-react-components/ -- https://github.com/kripod/react-polymorphic-box - -[Thanks @eps1lon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69) for this - -## Generic Components - -Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use the generic type to annotate types of any variables defined inside your function / class scope. - -```tsx -interface Props { - items: T[]; - renderItem: (item: T) => React.ReactNode; -} -function List(props: Props) { - const { items, renderItem } = props; - const [state, setState] = React.useState([]); // You can use type T in List function scope. - return ( -
    - {items.map(renderItem)} - - {JSON.stringify(state, null, 2)} -
    - ); -} -``` - -You can then use the generic components and get nice type safety through type inference: - -```tsx -ReactDOM.render( - ( -
  • - {/* Error: Property 'toPrecision' does not exist on type 'string'. */} - {item.toPrecision(3)} -
  • - )} - />, - document.body -); -``` - -As of [TS 2.9](#typescript-29), you can also supply the type parameter in your JSX to opt out of type inference: - -```tsx -ReactDOM.render( - - items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'. - renderItem={(item) =>
  • {item.toPrecision(3)}
  • } - />, - document.body -); -``` - -You can also use Generics using fat arrow function style: - -```tsx -interface Props { - items: T[]; - renderItem: (item: T) => React.ReactNode; -} - -// Note the before the function definition. -// You can't use just `` as it will confuse the TSX parser whether it's a JSX tag or a Generic Declaration. -// You can also use https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386 -const List = (props: Props) => { - const { items, renderItem } = props; - const [state, setState] = React.useState([]); // You can use type T in List function scope. - return ( -
    - {items.map(renderItem)} - - {JSON.stringify(state, null, 2)} -
    - ); -}; -``` - -The same for using classes: (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)'s [gist](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71)) - -```tsx -interface Props { - items: T[]; - renderItem: (item: T) => React.ReactNode; -} - -interface State { - items: T[]; -} - -class List extends React.PureComponent, State> { - // You can use type T inside List class. - state: Readonly> = { - items: [], - }; - render() { - const { items, renderItem } = this.props; - // You can use type T inside List class. - const clone: T[] = items.slice(0); - return ( -
    - {items.map(renderItem)} - - {JSON.stringify(this.state, null, 2)} -
    - ); - } -} -``` - -Though you can't use Generic Type Parameters for Static Members: - -```tsx -class List extends React.PureComponent, State> { - // Static members cannot reference class type parameters.ts(2302) - static getDerivedStateFromProps(props: Props, state: State) { - return { items: props.items }; - } -} -``` - -To fix this you need to convert your static function to a type inferred function: - -```tsx -class List extends React.PureComponent, State> { - static getDerivedStateFromProps(props: Props, state: State) { - return { items: props.items }; - } -} -``` - -### Generic components with children - -`children` is usually not defined as a part of the props type. Unless `children` are explicitly defined as a part of the `props` type, an attempt to use `props.children` in JSX or in the function body will fail: - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; -} - -/* Property 'children' does not exist on type 'WrapperProps'. */ -const Wrapper = (props: WrapperProps) => { - return ( -
    - {props.renderItem(props.item)} - {props.children} -
    - ); -}; - -/* -Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps'. - Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps'. -*/ - -const wrapper = ( - item}> - {test} - -); -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) - -To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; - children: string; // The component will only accept a single string child -} - -const Wrapper = (props: WrapperProps) => { - return ( -
    - {props.renderItem(props.item)} - {props.children} -
    - ); -}; -``` - -or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does): - -```tsx -interface WrapperProps { - item: T; - renderItem: (item: T) => React.ReactNode; -} - -const Wrapper = ( - props: React.PropsWithChildren> -) => { - return ( -
    - {props.renderItem(props.item)} - {props.children} -
    - ); -}; -``` - -## Typing a Component that Accepts Different Props - -Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". - -A very common use case for this is to render something as either a button or an anchor, based on if it receives a `href` attribute. - -```tsx -type ButtonProps = JSX.IntrinsicElements["button"]; -type AnchorProps = JSX.IntrinsicElements["a"]; - -// optionally use a custom type guard -function isPropsForAnchorElement( - props: ButtonProps | AnchorProps -): props is AnchorProps { - return "href" in props; -} - -function Clickable(props: ButtonProps | AnchorProps) { - if (isPropsForAnchorElement(props)) { - return
    ; - } else { - return
    - -You may also want to use Discriminated Unions, please check out [Expressive React Component APIs with Discriminated Unions](https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/). - -Here is a brief intuition for **Discriminated Union Types**: - -```ts -type UserTextEvent = { - type: "TextEvent"; - value: string; - target: HTMLInputElement; -}; -type UserMouseEvent = { - type: "MouseEvent"; - value: [number, number]; - target: HTMLElement; -}; -type UserEvent = UserTextEvent | UserMouseEvent; -function handle(event: UserEvent) { - if (event.type === "TextEvent") { - event.value; // string - event.target; // HTMLInputElement - return; - } - event.value; // [number, number] - event.target; // HTMLElement -} -``` - -
    - - Take care: TypeScript does not narrow the type of a Discriminated Union on the basis of typeof checks. The type guard has to be on the value of a key and not it's type. - - -```ts -type UserTextEvent = { value: string; target: HTMLInputElement }; -type UserMouseEvent = { value: [number, number]; target: HTMLElement }; -type UserEvent = UserTextEvent | UserMouseEvent; -function handle(event: UserEvent) { - if (typeof event.value === "string") { - event.value; // string - event.target; // HTMLInputElement | HTMLElement (!!!!) - return; - } - event.value; // [number, number] - event.target; // HTMLInputElement | HTMLElement (!!!!) -} -``` - -The above example does not work as we are not checking the value of `event.value` but only it's type. Read more about it [microsoft/TypeScript#30506 (comment)](https://github.com/microsoft/TypeScript/issues/30506#issuecomment-474858198) - -
    - -To streamline this you may also combine this with the concept of **User-Defined Type Guards**: - -```ts -function isString(a: unknown): a is string { - return typeof a === "string"; -} -``` - -[Read more about User-Defined Type Guards in the Handbook](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards). - -## Props: One or the Other but not Both - -Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both: - -```tsx -type Props1 = { foo: string }; -type Props2 = { bar: string }; - -function MyComponent(props: Props1 | Props2) { - if ("foo" in props) { - // props.bar // error - return
    {props.foo}
    ; - } else { - // props.foo // error - return
    {props.bar}
    ; - } -} -const UsageComponent: React.FC = () => ( -
    - - - {/* // invalid */} -
    -); -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) - -Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) - -## Props: Must Pass Both - -```tsx -type OneOrAnother = - | (T1 & { [K in keyof T2]?: undefined }) - | (T2 & { [K in keyof T1]?: undefined }); - -type Props = OneOrAnother<{ a: string; b: string }, {}>; - -const a: Props = { a: "a" }; // error -const b: Props = { b: "b" }; // error -const ab: Props = { a: "a", b: "b" }; // ok -``` - -Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) - -## Props: Can Optionally Pass One Only If the Other Is Passed - -Say you want a Text component that gets truncated if `truncate` prop is passed but expands to show the full text when `expanded` prop is passed (e.g. when the user clicks the text). - -You want to allow `expanded` to be passed only if `truncate` is also passed, because there is no use for `expanded` if the text is not truncated. - -You can do this by function overloads: - -```tsx -type CommonProps = { - children: React.ReactNode; - miscProps?: any; -}; - -type NoTruncateProps = CommonProps & { truncate?: false }; - -type TruncateProps = CommonProps & { truncate: true; expanded?: boolean }; - -// Function overloads to accept both prop types NoTruncateProps & TruncateProps -function Text(props: NoTruncateProps): JSX.Element; -function Text(props: TruncateProps): JSX.Element; -function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) { - const { children, truncate, expanded, ...otherProps } = props; - const classNames = truncate ? ".truncate" : ""; - return ( -
    - {children} -
    - ); -} -``` - -Using the Text component: - -```tsx -const App: React.FC = () => ( - <> - {/* these all typecheck */} - not truncated - truncated - - truncate-able but expanded - - {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */} - truncate-able but expanded - -); -``` - -## Omit attribute from a type - -Note: [Omit was added as a first class utility in TS 3.5](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk)! 🎉 - -Sometimes when intersecting types, we want to define our own version of an attribute. For example, I want my component to have a `label`, but the type I am intersecting with also has a `label` attribute. Here's how to extract that out: - -```tsx -export interface Props { - label: React.ReactNode; // this will conflict with the InputElement's label -} - -// this comes inbuilt with TS 3.5 -type Omit = Pick>; - -// usage -export const Checkbox = ( - props: Props & Omit, "label"> -) => { - const { label } = props; - return ( -
    - - {label} -
    - ); -}; -``` - -When your component defines multiple props, chances of those conflicts increase. However you can explicitly state that all your fields should be removed from the underlying component using the `keyof` operator: - -```tsx -export interface Props { - label: React.ReactNode; // conflicts with the InputElement's label - onChange: (text: string) => void; // conflicts with InputElement's onChange -} - -export const Textbox = ( - props: Props & Omit, keyof Props> -) => { - // implement Textbox component ... -}; -``` - -## Type Zoo - -As you can see from the Omit example above, you can write significant logic in your types as well. [type-zoo](https://github.com/pelotom/type-zoo) is a nice toolkit of operators you may wish to check out (includes Omit), as well as [utility-types](https://github.com/piotrwitek/utility-types) (especially for those migrating from Flow). - -## Extracting Prop Types of a Component - -_(Contributed by [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/63))_ - -There are a lot of places where you want to reuse some slices of props because of prop drilling, -so you can either export the props type as part of the module or extract them (either way works). - -The advantage of extracting the prop types is that you won't need to export everything, and a refactor of the source of truth component will propagate to all consuming components. - -```ts -import { ComponentProps, JSXElementConstructor } from "react"; - -// goes one step further and resolves with propTypes and defaultProps properties -type ApparentComponentProps< - C extends keyof JSX.IntrinsicElements | JSXElementConstructor -> = C extends JSXElementConstructor - ? JSX.LibraryManagedAttributes - : ComponentProps; -``` - -You can also use them to strongly type custom event handlers if they're not written at the call sites themselves -(i.e. inlined with the JSX attribute): - -```tsx -// my-inner-component.tsx -export function MyInnerComponent(props: { - onSomeEvent( - event: ComplexEventObj, - moreArgs: ComplexArgs - ): SomeWeirdReturnType; -}) { - /* ... */ -} - -// my-consuming-component.tsx -export function MyConsumingComponent() { - // event and moreArgs are contextually typed along with the return value - const theHandler: Props["onSomeEvent"] = ( - event, - moreArgs - ) => {}; - return ; -} -``` - -## Handling Exceptions - -You can provide good information when bad things happen. - -```ts -class InvalidDateFormatError extends RangeError {} -class DateIsInFutureError extends RangeError {} - -/** - * // optional docblock - * @throws {InvalidDateFormatError} The user entered date incorrectly - * @throws {DateIsInFutureError} The user entered date in future - * - */ -function parse(date: string) { - if (!isValid(date)) - throw new InvalidDateFormatError("not a valid date format"); - if (isInFuture(date)) throw new DateIsInFutureError("date is in the future"); - // ... -} - -try { - // call parse(date) somewhere -} catch (e) { - if (e instanceof InvalidDateFormatError) { - console.error("invalid date format", e); - } else if (e instanceof DateIsInFutureError) { - console.warn("date is in future", e); - } else { - throw e; - } -} -``` - -[View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) - -Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing: - -```ts -function parse( - date: string -): Date | InvalidDateFormatError | DateIsInFutureError { - if (!isValid(date)) - return new InvalidDateFormatError("not a valid date format"); - if (isInFuture(date)) return new DateIsInFutureError("date is in the future"); - // ... -} - -// now consumer *has* to handle the errors -let result = parse("mydate"); -if (result instanceof InvalidDateFormatError) { - console.error("invalid date format", result.message); -} else if (result instanceof DateIsInFutureError) { - console.warn("date is in future", result.message); -} else { - /// use result safely -} - -// alternately you can just handle all errors -if (result instanceof Error) { - console.error("error", result); -} else { - /// use result safely -} -``` - -You can also describe exceptions with special-purpose data types (don't say monads...) like the `Try`, `Option` (or `Maybe`), and `Either` data types: - -```ts -interface Option { - flatMap(f: (value: T) => None): None; - flatMap(f: (value: T) => Option): FormikOption; - getOrElse(value: T): T; -} -class Some implements Option { - constructor(private value: T) {} - flatMap(f: (value: T) => None): None; - flatMap(f: (value: T) => Some): Some; - flatMap(f: (value: T) => Option): Option { - return f(this.value); - } - getOrElse(): T { - return this.value; - } -} -class None implements Option { - flatMap(): None { - return this; - } - getOrElse(value: U): U { - return value; - } -} - -// now you can use it like: -let result = Option(6) // Some - .flatMap((n) => Option(n * 3)) // Some - .flatMap((n = new None())) // None - .getOrElse(7); - -// or: -let result = ask() // Option - .flatMap(parse) // Option - .flatMap((d) => new Some(d.toISOString())) // Option - .getOrElse("error parsing string"); -``` - -## Third Party Libraries - -Sometimes DefinitelyTyped can get it wrong, or isn't quite addressing your use case. You can declare your own file with the same interface name. Typescript will merge interfaces with the same name. - -# Section 2: Useful Patterns by TypeScript Version - -TypeScript Versions often introduce new ways to do things; this section helps current users of React + TypeScript upgrade TypeScript versions and explore patterns commonly used by TypeScript + React apps and libraries. This may have duplications with other sections; if you spot any discrepancies, [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new)! - -_TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!_ Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). For more TypeScript history, see [A Brief History of TypeScript Types](https://github.com/blakeembrey/a-brief-history-of-types-with-typescript) and [A Brief History of DefinitelyTyped](https://blog.johnnyreilly.com/2019/10/definitely-typed-movie.html) - -## TypeScript 2.9 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/05/31/announcing-typescript-2-9/)] - -1. Type arguments for tagged template strings (e.g. `styled-components`): - -```tsx -export interface InputFormProps { - foo: string; // this is understood inside the template string below -} - -export const InputForm = styledInput` - color: - ${({ themeName }) => (themeName === "dark" ? "black" : "white")}; - border-color: ${({ foo }) => (foo ? "red" : "black")}; -`; -``` - -2. **JSX Generics** - -https://github.com/Microsoft/TypeScript/pull/22415 - -Helps with typing/using generic components: - -```tsx -// instead of -) => { - /* your code here ... */ - }} -/>; - -// usage - - render={(props) => { - /* your code here ... */ - }} -/>; - data={12} />; -``` - -More info: https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components - -## TypeScript 3.0 - -[[Release Notes](https://github.com/Microsoft/TypeScript/releases/tag/v3.0.1) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/)] - -1. Typed rest parameters for writing arguments of variable length: - -```ts -// `rest` accepts any number of strings - even none! -function foo(...rest: string[]) { - // ... -} - -foo("hello"); // works -foo("hello", "world"); // also works -``` - -2. Support for `propTypes` and `static defaultProps` in JSX using `LibraryManagedAttributes`: - -```tsx -export interface Props { - name: string; -} - -export class Greet extends React.Component { - render() { - const { name } = this.props; - return
    Hello ${name.toUpperCase()}!
    ; - } - static defaultProps = { name: "world" }; -} - -// Type-checks! No type assertions needed! -let el = ; -``` - -3. new `Unknown` type - -For typing API's to force type checks - not specifically React related, however very handy for dealing with API responses: - -```tsx -interface IComment { - date: Date; - message: string; -} - -interface IDataService1 { - getData(): any; -} - -let service1: IDataService1; -const response = service1.getData(); -response.a.b.c.d; // RUNTIME ERROR - -// ----- compare with ------- - -interface IDataService2 { - getData(): unknown; // ooo -} - -let service2: IDataService2; -const response2 = service2.getData(); -// response2.a.b.c.d; // COMPILE TIME ERROR if you do this - -if (typeof response === "string") { - console.log(response.toUpperCase()); // `response` now has type 'string' -} -``` - -TODO: blame this change. Don't know what this shouldve done - -You can also assert a type, or use a **type guard** against an `unknown` type. This is better than resorting to `any`. - -4. Project References - -Project references allow TypeScript projects to depend on other TypeScript projects – specifically, allowing tsconfig.json files to reference other tsconfig.json files. This lets large codebases scale without recompiling every part of the codebase every time, by breaking it up into multiple projects. - -In each folder, create a tsconfig.json that includes at least: - -```json -{ - "compilerOptions": { - "composite": true, // tells TSC it is a subproject of a larger project - "declaration": true, // emit .d.ts declaration files since project references dont have access to source ts files. important for project references to work! - "declarationMap": true, // sourcemaps for .d.ts - "rootDir": "." // specify compile it relative to root project at . - }, - "include": ["./**/*.ts"], - "references": [ - // (optional) array of subprojects your subproject depends on - { - "path": "../myreferencedproject", // must have tsconfig.json - "prepend": true // concatenate js and sourcemaps generated by this subproject, if and only if using outFile - } - ] -} -``` - -and the root `tsconfig.json` that references top level subproject: - -```json -{ - "files": [], - "references": [{ "path": "./proj1" }, { "path": "./proj2" }] -} -``` - -and you must run `tsc --build` or `tsc -b`. - -To save the tsconfig boilerplate, you can use the `extends` option: - -```json -{ - "extends": "../tsconfig.base" - // more stuff here -} -``` - -## TypeScript 3.1 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/announcing-typescript-3-1/)] - -1. Properties declarations on functions - -Attaching properties to functions like this "just works" now: - -```tsx -export const FooComponent = ({ name }) =>
    Hello! I am {name}
    ; - -FooComponent.defaultProps = { - name: "swyx", -}; -``` - -## TypeScript 3.2 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/11/29/announcing-typescript-3-2/)] - -nothing specifically React related. - -## TypeScript 3.3 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-3.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2019/01/31/announcing-typescript-3-3/)] - -nothing specifically React related. - -## TypeScript 3.4 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4)] - -1. [`const` assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions) - -```tsx -export function useLoading() { - const [isLoading, setState] = React.useState(false); - const load = (aPromise: Promise) => { - setState(true); - return aPromise.finally(() => setState(false)); - }; - return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] -} -``` - -More info on places you can use [const assertions](https://blog.logrocket.com/const-assertions-are-the-killer-new-typescript-feature-b73451f35802). - -## TypeScript 3.5 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/)] - -1. Built-in `` Type!! - -2. Higher order type inference from generic constructors - -```tsx -type ComponentClass

    = new (props: P) => Component

    ; -declare class Component

    { - props: P; - constructor(props: P); -} - -declare function myHoc

    (C: ComponentClass

    ): ComponentClass

    ; - -type NestedProps = { foo: number; stuff: T }; - -declare class GenericComponent extends Component> {} - -// type is 'new (props: NestedProps) => Component>' -const GenericComponent2 = myHoc(GenericComponent); -``` - -See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272) - -## TypeScript 3.6 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)] - -Nothing particularly React specific but [the playground](https://github.com/agentcooper/typescript-play) got an upgrade and [Ambient Classes and Functions Can Merge](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge) - -## TypeScript 3.7 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)] - -1. Optional Chaining - -```ts -let x = foo?.bar.baz(); - -// is equivalent to - -let x = foo === null || foo === undefined ? undefined : foo.bar.baz(); - -// Optional Element access -function tryGetFirstElement(arr?: T[]) { - return arr?.[0]; -} - -// Optional Call -async function makeRequest(url: string, log?: (msg: string) => void) { - log?.(`Request started at ${new Date().toISOString()}`); - const result = (await fetch(url)).json(); - log?.(`Request finished at at ${new Date().toISOString()}`); - return result; -} -``` - -2. Nullish Coalescing - -```ts -let x = foo ?? bar(); - -// equivalent to - -let x = foo !== null && foo !== undefined ? foo : bar(); -``` - -**YOU SHOULD USUALLY USE `??` WHEREVER YOU NORMALLY USE `||`** unless you truly mean falsiness: - -```tsx -function ShowNumber({ value }: { value: number }) { - let _value = value || 0.5; // will replace 0 with 0.5 even if user really means 0 - // etc... -} -``` - -3. Assertion Functions - -```tsx -function assert(condition: any, msg?: string): asserts condition { - if (!condition) { - throw new AssertionError(msg); - } -} -function yell(str) { - assert(typeof str === "string"); - - return str.toUppercase(); - // ~~~~~~~~~~~ - // error: Property 'toUppercase' does not exist on type 'string'. - // Did you mean 'toUpperCase'? -} -``` - -You can also assert without a custom function: - -```tsx -function assertIsString(val: any): asserts val is string { - if (typeof val !== "string") { - throw new AssertionError("Not a string!"); - } -} -function yell(str: any) { - assertIsString(str); - - // Now TypeScript knows that 'str' is a 'string'. - - return str.toUppercase(); - // ~~~~~~~~~~~ - // error: Property 'toUppercase' does not exist on type 'string'. - // Did you mean 'toUpperCase'? -} -``` - -4. `ts-nocheck` - -You can now add `// @ts-nocheck` to the top of TypeScript files! good for migrations. - -## TypeScript 3.8 - -[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/)] - -1. Type-Only Imports and Exports - -```ts -import type { SomeThing } from "./some-module.js"; - -export type { SomeThing }; -``` - -2. ECMAScript Private Fields - -Not really React specific but ok Bloomberg - -3. `export * as ns` Syntax - -This is ES2020 syntax. Instead of - -```js -import * as utilities from "./utilities.js"; -export { utilities }; -``` - -you can do - -```js -export * as utilities from "./utilities.js"; -``` - -4. Top-Level `await` - -not React specific but gj Myles - -5. JSDoc Property Modifiers - -handy for JSDoc users - `@public, @private, @protected, @readonly` - -6. Better Directory Watching on Linux and watchOptions -7. “Fast and Loose” Incremental Checking - -`assumeChangesOnlyAffectDirectDependencies` reduces build times for extremely large codebases. - -## TypeScript Roadmap and Spec - -https://github.com/Microsoft/TypeScript/wiki/Roadmap - -Did you also know you can read the TypeScript spec online?? https://github.com/microsoft/TypeScript/blob/master/doc/spec.md - -# Section 3: Misc. Concerns - -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. - -## Writing TypeScript Libraries instead of Apps - -`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. - -```ts -interface IMyComponentProps { - autoHeight: boolean; - secondProp: number; -} - -export class MyComponent extends React.Component { - static propTypes = { - autoHeight: PropTypes.bool, - secondProp: PropTypes.number.isRequired, - }; -} -``` - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Commenting Components - -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. - -```tsx -import React from "react"; - -interface MyProps { - /** Description of prop "label". - * @default foobar - * */ - label?: string; -} - -/** - * General component description in JSDoc format. Markdown is *supported*. - */ -export default function MyComponent({ label = "foobar" }: MyProps) { - return

    Hello world {label}
    ; -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Namespaced Components - -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()`; - -```tsx -import React from "react"; - -const Input = (props: any) => ; - -const Form = React.forwardRef( - ({ children, ...otherProps }, ref) => ( - - {children} - - ) -); - -/** - * Exported components now can be used as `
    + +You may also want to use Discriminated Unions, please check out [Expressive React Component APIs with Discriminated Unions](https://blog.andrewbran.ch/expressive-react-component-apis-with-discriminated-unions/). + +Here is a brief intuition for **Discriminated Union Types**: + +```ts +type UserTextEvent = { + type: "TextEvent"; + value: string; + target: HTMLInputElement; +}; +type UserMouseEvent = { + type: "MouseEvent"; + value: [number, number]; + target: HTMLElement; +}; +type UserEvent = UserTextEvent | UserMouseEvent; +function handle(event: UserEvent) { + if (event.type === "TextEvent") { + event.value; // string + event.target; // HTMLInputElement + return; + } + event.value; // [number, number] + event.target; // HTMLElement +} +``` + +
    + + Take care: TypeScript does not narrow the type of a Discriminated Union on the basis of typeof checks. The type guard has to be on the value of a key and not it's type. + + +```ts +type UserTextEvent = { value: string; target: HTMLInputElement }; +type UserMouseEvent = { value: [number, number]; target: HTMLElement }; +type UserEvent = UserTextEvent | UserMouseEvent; +function handle(event: UserEvent) { + if (typeof event.value === "string") { + event.value; // string + event.target; // HTMLInputElement | HTMLElement (!!!!) + return; + } + event.value; // [number, number] + event.target; // HTMLInputElement | HTMLElement (!!!!) +} +``` + +The above example does not work as we are not checking the value of `event.value` but only it's type. Read more about it [microsoft/TypeScript#30506 (comment)](https://github.com/microsoft/TypeScript/issues/30506#issuecomment-474858198) + +
    + +To streamline this you may also combine this with the concept of **User-Defined Type Guards**: + +```ts +function isString(a: unknown): a is string { + return typeof a === "string"; +} +``` + +[Read more about User-Defined Type Guards in the Handbook](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards). diff --git a/docs/advanced/guides/type-zoo.md b/docs/advanced/guides/type-zoo.md new file mode 100644 index 00000000..eb136c59 --- /dev/null +++ b/docs/advanced/guides/type-zoo.md @@ -0,0 +1,6 @@ +--- +id: type_zoo +title: Type Zoo +--- + +As you can see from the Omit example above, you can write significant logic in your types as well. [type-zoo](https://github.com/pelotom/type-zoo) is a nice toolkit of operators you may wish to check out (includes Omit), as well as [utility-types](https://github.com/piotrwitek/utility-types) (especially for those migrating from Flow). diff --git a/docs/advanced/index.md b/docs/advanced/index.md new file mode 100644 index 00000000..bab17304 --- /dev/null +++ b/docs/advanced/index.md @@ -0,0 +1,18 @@ +--- +id: intro +sidebar_label: Intro +title: Advanced Cheatsheet +--- + +**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**. + +- It also has miscellaneous tips and tricks for pro users. +- Advice for contributing to DefinitelyTyped +- The goal is to take _full advantage_ of TypeScript. + +**Creating React + TypeScript Libraries** + +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. + +- Be sure to also check [`basarat`'s guide](https://basarat.gitbooks.io/typescript/content/docs/quick/library.html) for library tsconfig settings. +- From the Angular world, check out https://github.com/bitjson/typescript-starter diff --git a/docs/advanced/misc-concerns.md b/docs/advanced/misc-concerns.md new file mode 100644 index 00000000..cd0a1c61 --- /dev/null +++ b/docs/advanced/misc-concerns.md @@ -0,0 +1,186 @@ +--- +id: misc_concerns +title: Section 3: Misc. Concerns +sidebar_label: Misc. Concerns +--- + +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. + +## Writing TypeScript Libraries instead of Apps + +`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. + +```ts +interface IMyComponentProps { + autoHeight: boolean; + secondProp: number; +} + +export class MyComponent extends React.Component { + static propTypes = { + autoHeight: PropTypes.bool, + secondProp: PropTypes.number.isRequired, + }; +} +``` + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + +## Commenting Components + +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. + +```tsx +import React from "react"; + +interface MyProps { + /** Description of prop "label". + * @default foobar + * */ + label?: string; +} + +/** + * General component description in JSDoc format. Markdown is *supported*. + */ +export default function MyComponent({ label = "foobar" }: MyProps) { + return
    Hello world {label}
    ; +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgFkBPABRzAGc4BvCnDgB6AFRi4AESQ80UYGBjAI1OBExww3OACIANigBGSfboB0Q4ZIACAEySMArvqwQIRlFCtxJYkVaGJvoA-ABccDwwCtQA5gDcFAC+FBTiYkKSAOJI1PQo+nBouJB5tHAOcgpKKmo0cABSAMpSEGhwmNAgKDDmrF4A1nYQAO51fGI8TmCQsEh2YpbkvgHkSAAes-AOzq4dTtQYtaxsAMIlqrkwABT8cEGmcAC8ep0eXrpwSRHsXBC8AEoBFYiDAnFA1AAeOzAABuAD4ABKmfQQOAjaD6OwCB76JKQkQwhGJchJIA) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + +## Namespaced Components + +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()`; + +```tsx +import React from "react"; + +const Input = (props: any) => ; + +const Form = React.forwardRef( + ({ children, ...otherProps }, ref) => ( +
    + {children} +
    + ) +); + +/** + * Exported components now can be used as `
    ` and `` + */ +export default Object.assign(Form, { Input: Input }); +``` + +[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) + +(Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165)) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + +## Design System Development + +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) + +For developing with Storybook, read the docs I wrote over here: . This includes automatic proptype documentation generation, which is awesome :) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + +## Migrating From Flow + +You should check out large projects that are migrating from flow to pick up concerns and tips: + +- [Jest](https://github.com/facebook/jest/pull/7554) +- [Expo](https://github.com/expo/expo/issues/2164) +- [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982) +- [Storybook](https://github.com/storybooks/storybook/issues/5030) +- [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf) + +Useful libraries: + +- +- +- + +If you have specific advice in this area, please file a PR! + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). + +## Prettier + +There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! + +```bash +$ yarn add -D prettier husky lint-staged +``` + +```json +// inside package.json +{ + //... + "husky": { + "hooks": { + "pre-commit": "lint-staged" + } + }, + "lint-staged": { + "linters": { + "src/*.{ts,tsx,js,jsx,css,scss,md}": [ + "prettier --trailing-comma es5 --single-quote --write", + "git add" + ], + "ignore": ["**/dist/*, **/node_modules/*"] + } + }, + "prettier": { + "printWidth": 80, + "semi": false, + "singleQuote": true, + "trailingComma": "es5" + } +} +``` + +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) + +For library authors, this is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). + +## Testing + +Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions: + +- https://github.com/azz/jest-runner-tsc +- https://github.com/SamVerschueren/tsd +- https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) +- https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)) + +## Working with Non-TypeScript Libraries (writing your own index.d.ts) + +Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: + +``` +[ts] +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. + Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016] +``` + +So create a `.d.ts` file anywhere in your project with the module definition: + +```ts +// de-indent.d.ts +declare module "de-indent" { + function deindent(): void; + export = deindent; // default export +} +``` + +
    + +Further Discussion + +Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/8). We have more discussion and examples [in our issue here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12). + +
    diff --git a/docs/advanced/patterns.md b/docs/advanced/patterns.md new file mode 100644 index 00000000..d8c833df --- /dev/null +++ b/docs/advanced/patterns.md @@ -0,0 +1,387 @@ +--- +id: patterns +title: Section 2: Useful Patterns by TypeScript Version +sidebar_label: Useful Patterns by TypeScript Version +--- + +TypeScript Versions often introduce new ways to do things; this section helps current users of React + TypeScript upgrade TypeScript versions and explore patterns commonly used by TypeScript + React apps and libraries. This may have duplications with other sections; if you spot any discrepancies, [file an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new)! + +_TypeScript version guides before 2.9 are unwritten, please feel free to send a PR!_ Apart from official TS team communication we also recommend [Marius Schulz's blog for version notes](https://mariusschulz.com/). For more TypeScript history, see [A Brief History of TypeScript Types](https://github.com/blakeembrey/a-brief-history-of-types-with-typescript) and [A Brief History of DefinitelyTyped](https://blog.johnnyreilly.com/2019/10/definitely-typed-movie.html) + +## TypeScript 2.9 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/05/31/announcing-typescript-2-9/)] + +1. Type arguments for tagged template strings (e.g. `styled-components`): + +```tsx +export interface InputFormProps { + foo: string; // this is understood inside the template string below +} + +export const InputForm = styledInput` + color: + ${({ themeName }) => (themeName === "dark" ? "black" : "white")}; + border-color: ${({ foo }) => (foo ? "red" : "black")}; +`; +``` + +2. **JSX Generics** + +https://github.com/Microsoft/TypeScript/pull/22415 + +Helps with typing/using generic components: + +```tsx +// instead of +) => { + /* your code here ... */ + }} +/>; + +// usage + + render={(props) => { + /* your code here ... */ + }} +/>; + data={12} />; +``` + +More info: https://github.com/basarat/typescript-book/blob/master/docs/jsx/react.md#react-jsx-tip-generic-components + +## TypeScript 3.0 + +[[Release Notes](https://github.com/Microsoft/TypeScript/releases/tag/v3.0.1) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/07/30/announcing-typescript-3-0/)] + +1. Typed rest parameters for writing arguments of variable length: + +```ts +// `rest` accepts any number of strings - even none! +function foo(...rest: string[]) { + // ... +} + +foo("hello"); // works +foo("hello", "world"); // also works +``` + +2. Support for `propTypes` and `static defaultProps` in JSX using `LibraryManagedAttributes`: + +```tsx +export interface Props { + name: string; +} + +export class Greet extends React.Component { + render() { + const { name } = this.props; + return
    Hello ${name.toUpperCase()}!
    ; + } + static defaultProps = { name: "world" }; +} + +// Type-checks! No type assertions needed! +let el = ; +``` + +3. new `Unknown` type + +For typing API's to force type checks - not specifically React related, however very handy for dealing with API responses: + +```tsx +interface IComment { + date: Date; + message: string; +} + +interface IDataService1 { + getData(): any; +} + +let service1: IDataService1; +const response = service1.getData(); +response.a.b.c.d; // RUNTIME ERROR + +// ----- compare with ------- + +interface IDataService2 { + getData(): unknown; // ooo +} + +let service2: IDataService2; +const response2 = service2.getData(); +// response2.a.b.c.d; // COMPILE TIME ERROR if you do this + +if (typeof response === "string") { + console.log(response.toUpperCase()); // `response` now has type 'string' +} +``` + +TODO: blame this change. Don't know what this shouldve done + +You can also assert a type, or use a **type guard** against an `unknown` type. This is better than resorting to `any`. + +4. Project References + +Project references allow TypeScript projects to depend on other TypeScript projects – specifically, allowing tsconfig.json files to reference other tsconfig.json files. This lets large codebases scale without recompiling every part of the codebase every time, by breaking it up into multiple projects. + +In each folder, create a tsconfig.json that includes at least: + +```json +{ + "compilerOptions": { + "composite": true, // tells TSC it is a subproject of a larger project + "declaration": true, // emit .d.ts declaration files since project references dont have access to source ts files. important for project references to work! + "declarationMap": true, // sourcemaps for .d.ts + "rootDir": "." // specify compile it relative to root project at . + }, + "include": ["./**/*.ts"], + "references": [ + // (optional) array of subprojects your subproject depends on + { + "path": "../myreferencedproject", // must have tsconfig.json + "prepend": true // concatenate js and sourcemaps generated by this subproject, if and only if using outFile + } + ] +} +``` + +and the root `tsconfig.json` that references top level subproject: + +```json +{ + "files": [], + "references": [{ "path": "./proj1" }, { "path": "./proj2" }] +} +``` + +and you must run `tsc --build` or `tsc -b`. + +To save the tsconfig boilerplate, you can use the `extends` option: + +```json +{ + "extends": "../tsconfig.base" + // more stuff here +} +``` + +## TypeScript 3.1 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-1.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/announcing-typescript-3-1/)] + +1. Properties declarations on functions + +Attaching properties to functions like this "just works" now: + +```tsx +export const FooComponent = ({ name }) =>
    Hello! I am {name}
    ; + +FooComponent.defaultProps = { + name: "swyx", +}; +``` + +## TypeScript 3.2 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2018/11/29/announcing-typescript-3-2/)] + +nothing specifically React related. + +## TypeScript 3.3 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-3.html) | [Blog Post](https://blogs.msdn.microsoft.com/typescript/2019/01/31/announcing-typescript-3-3/)] + +nothing specifically React related. + +## TypeScript 3.4 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-4.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4)] + +1. [`const` assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions) + +```tsx +export function useLoading() { + const [isLoading, setState] = React.useState(false); + const load = (aPromise: Promise) => { + setState(true); + return aPromise.finally(() => setState(false)); + }; + return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] +} +``` + +More info on places you can use [const assertions](https://blog.logrocket.com/const-assertions-are-the-killer-new-typescript-feature-b73451f35802). + +## TypeScript 3.5 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-5/)] + +1. Built-in `` Type!! + +2. Higher order type inference from generic constructors + +```tsx +type ComponentClass

    = new (props: P) => Component

    ; +declare class Component

    { + props: P; + constructor(props: P); +} + +declare function myHoc

    (C: ComponentClass

    ): ComponentClass

    ; + +type NestedProps = { foo: number; stuff: T }; + +declare class GenericComponent extends Component> {} + +// type is 'new (props: NestedProps) => Component>' +const GenericComponent2 = myHoc(GenericComponent); +``` + +See also [Notes from Google upgrading to 3.5](https://github.com/microsoft/TypeScript/issues/33272) + +## TypeScript 3.6 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-6/)] + +Nothing particularly React specific but [the playground](https://github.com/agentcooper/typescript-play) got an upgrade and [Ambient Classes and Functions Can Merge](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-6.html#ambient-classes-and-functions-can-merge) + +## TypeScript 3.7 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-7/)] + +1. Optional Chaining + +```ts +let x = foo?.bar.baz(); + +// is equivalent to + +let x = foo === null || foo === undefined ? undefined : foo.bar.baz(); + +// Optional Element access +function tryGetFirstElement(arr?: T[]) { + return arr?.[0]; +} + +// Optional Call +async function makeRequest(url: string, log?: (msg: string) => void) { + log?.(`Request started at ${new Date().toISOString()}`); + const result = (await fetch(url)).json(); + log?.(`Request finished at at ${new Date().toISOString()}`); + return result; +} +``` + +2. Nullish Coalescing + +```ts +let x = foo ?? bar(); + +// equivalent to + +let x = foo !== null && foo !== undefined ? foo : bar(); +``` + +**YOU SHOULD USUALLY USE `??` WHEREVER YOU NORMALLY USE `||`** unless you truly mean falsiness: + +```tsx +function ShowNumber({ value }: { value: number }) { + let _value = value || 0.5; // will replace 0 with 0.5 even if user really means 0 + // etc... +} +``` + +3. Assertion Functions + +```tsx +function assert(condition: any, msg?: string): asserts condition { + if (!condition) { + throw new AssertionError(msg); + } +} +function yell(str) { + assert(typeof str === "string"); + + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? +} +``` + +You can also assert without a custom function: + +```tsx +function assertIsString(val: any): asserts val is string { + if (typeof val !== "string") { + throw new AssertionError("Not a string!"); + } +} +function yell(str: any) { + assertIsString(str); + + // Now TypeScript knows that 'str' is a 'string'. + + return str.toUppercase(); + // ~~~~~~~~~~~ + // error: Property 'toUppercase' does not exist on type 'string'. + // Did you mean 'toUpperCase'? +} +``` + +4. `ts-nocheck` + +You can now add `// @ts-nocheck` to the top of TypeScript files! good for migrations. + +## TypeScript 3.8 + +[[Release Notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html) | [Blog Post](https://devblogs.microsoft.com/typescript/announcing-typescript-3-8/)] + +1. Type-Only Imports and Exports + +```ts +import type { SomeThing } from "./some-module.js"; + +export type { SomeThing }; +``` + +2. ECMAScript Private Fields + +Not really React specific but ok Bloomberg + +3. `export * as ns` Syntax + +This is ES2020 syntax. Instead of + +```js +import * as utilities from "./utilities.js"; +export { utilities }; +``` + +you can do + +```js +export * as utilities from "./utilities.js"; +``` + +4. Top-Level `await` + +not React specific but gj Myles + +5. JSDoc Property Modifiers + +handy for JSDoc users - `@public, @private, @protected, @readonly` + +6. Better Directory Watching on Linux and watchOptions +7. “Fast and Loose” Incremental Checking + +`assumeChangesOnlyAffectDirectDependencies` reduces build times for extremely large codebases. + +## TypeScript Roadmap and Spec + +https://github.com/Microsoft/TypeScript/wiki/Roadmap + +Did you also know you can read the TypeScript spec online?? https://github.com/microsoft/TypeScript/blob/master/doc/spec.md diff --git a/docs/advanced/types-react-ap.md b/docs/advanced/types-react-ap.md new file mode 100644 index 00000000..ea4ac602 --- /dev/null +++ b/docs/advanced/types-react-ap.md @@ -0,0 +1,90 @@ +--- +id: types_react_api +title: Section 4: @types/react and @types/react-dom APIs +sidebar_label: @types/react and @types/react-dom APIs +--- + +The `@types` typings export both "public" types meant for your use as well as "private" types that are for internal use. + +Check [SaltyCrane's React TypeScript Cheatsheet](https://github.com/saltycrane/typescript-cheatsheet) for a nice autogenerated complete reference. + +## `@types/react` + +[Link to `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts) + +**Namespace: React** + +Most Commonly Used Interfaces and Types + +- `ReactNode` - anything that is renderable _inside_ of JSX, this is NOT the same as what can be rendered by a component! +- `Component` - base class of all class-based components +- `PureComponent` - base class for all class-based optimized components +- `FC`, `FunctionComponent` - a complete interface for function components, often used to type external components instead of typing your own +- `CSSProperties` - used to type style objects +- all events: used to type event handlers +- all event handlers: used to type event handlers +- all consts: `Children`, `Fragment`, ... are all public and reflect the React runtime namespace + +Not Commonly Used but Good to know + +- `Ref` - used to type `innerRef` +- `ElementType` - used for higher order components or operations on components +- `ComponentType` - used for higher order components where you don't specifically deal with the intrinsic components +- `ReactPortal` - used if you specifically need to type a prop as a portal, otherwise it is part of `ReactNode` +- `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 +- `JSXElementConstructor` - anything that TypeScript considers to be a valid thing that can go into the opening tag of a JSX expression +- `ComponentProps` - props of a component +- `ComponentPropsWithRef` - props of a component where if it is a class-based component it will replace the `ref` prop with its own instance type +- `ComponentPropsWithoutRef` - props of a component without its `ref` prop +- all methods: `createElement`, `cloneElement`, ... are all public and reflect the React runtime API + +[@Ferdaber's note](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69): I discourage the use of most `...Element` types because of how black-boxy `JSX.Element` is. You should almost always assume that anything produced by `React.createElement` is the base type `React.ReactElement`. + +**Namespace: JSX** + +- `Element` - the type of any JSX expression +- `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. +- `IntrinsicElements` - every possible built-in component that can be typed in as a lowercase tag name in JSX + +Not commonly used but good to know + +- `IntrinsicAttributes` set of attributes that all `IntrinsicElements` support... basically just `key`. +- `ElementChildrenAttribute` name of property that TS looks at to figure out what types of children a component supports. Basically the `children` property +- `ElementAttributesProperty` name of property that TS looks at to figure out what attributes a component supports. Basically the `props` property (for a class instance) + +**Don't use/Internal/Deprecated** + +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. + +- `SFCElement` +- `SFC` +- `ComponentState` +- `LegacyRef` +- `StatelessComponent` +- `ReactType` + +### 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 +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 +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`. + +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: + +```ts +// react-unstable-attributes.d.ts +import "react"; + +declare module "react" { + interface ImgHTMLAttributes extends HTMLAttributes { + loading?: "auto" | "eager" | "lazy"; + } +} +``` + +## `@types/react-dom` + +To be written diff --git a/docs/advanced/utility-types.md b/docs/advanced/utility-types.md new file mode 100644 index 00000000..d67a2c1b --- /dev/null +++ b/docs/advanced/utility-types.md @@ -0,0 +1,7 @@ +--- +id: utility_types +title: Section 0: Utility Types +sidebar_label: Utility Types +--- + +We will assume knowledge of utility types covered in the sister project [`typescript-utilities-guide`](https://github.com/typescript-cheatsheets/typescript-utilities-guide). Look up libraries included there as well for your typing needs. diff --git a/docs/basic/editor-integration.md b/docs/basic/editor-integration.md new file mode 100644 index 00000000..3d976dcb --- /dev/null +++ b/docs/basic/editor-integration.md @@ -0,0 +1,15 @@ +--- +id: editor_integration +title: Editor Tooling and Integration +--- + +- VSCode + - swyx's VSCode Extension: https://github.com/sw-yx/swyx-react-typescript-snippets + - amVim: https://marketplace.visualstudio.com/items?itemName=auiworks.amvim +- VIM + - https://github.com/Quramy/tsuquyomi + - nvim-typescript? + - https://github.com/leafgarland/typescript-vim + - peitalin/vim-jsx-typescript + - NeoVim: https://github.com/neoclide/coc.nvim + - other discussion: https://mobile.twitter.com/ryanflorence/status/1085715595994095620 diff --git a/docs/basic/examples.md b/docs/basic/examples.md new file mode 100644 index 00000000..47ed105e --- /dev/null +++ b/docs/basic/examples.md @@ -0,0 +1,7 @@ +--- +id: examples +title: Example App +sidebar_label: Examples +--- + +- [Create React App TypeScript Todo Example 2020](https://github.com/laststance/create-react-app-typescript-todo-example-2020) diff --git a/docs/basic/getting-started/basic-type-examples.md b/docs/basic/getting-started/basic-type-examples.md new file mode 100644 index 00000000..a3bc27a1 --- /dev/null +++ b/docs/basic/getting-started/basic-type-examples.md @@ -0,0 +1,41 @@ +--- +id: basic_type_example +title: Basic Prop Types Examples +--- + +```tsx +type AppProps = { + message: string; + count: number; + disabled: boolean; + /** array of a type! */ + names: string[]; + /** string literals to specify exact string values, with a union type to join them together */ + status: "waiting" | "success"; + /** any object as long as you dont use its properties (not common) */ + obj: object; + obj2: {}; // almost the same as `object`, exactly the same as `Object` + /** an object with defined properties (preferred) */ + obj3: { + id: string; + title: string; + }; + /** array of objects! (common) */ + objArr: { + id: string; + title: string; + }[]; + /** any function as long as you don't invoke it (not recommended) */ + onSomething: Function; + /** function that doesn't take or return anything (VERY COMMON) */ + onClick: () => void; + /** function with named prop (VERY COMMON) */ + onChange: (id: number) => void; + /** alternative function type syntax that takes an event (VERY COMMON) */ + onClick(event: React.MouseEvent): void; + /** an optional prop (VERY COMMON!) */ + optional?: OptionalType; +}; +``` + +Notice we have used the TSDoc `/** comment */` style here on each prop. You can and are encouraged to leave descriptive comments on reusable components. For a fuller example and discussion, see our [Commenting Components](/ADVANCED.md#commenting-components) section in the Advanced Cheatsheet. diff --git a/docs/basic/getting-started/class-components.md b/docs/basic/getting-started/class-components.md new file mode 100644 index 00000000..a06f55b6 --- /dev/null +++ b/docs/basic/getting-started/class-components.md @@ -0,0 +1,109 @@ +--- +id: class_components +title: Class Components +--- + +Within TypeScript, `React.Component` is a generic type (aka `React.Component`), so you want to provide it with (optional) prop and state type parameters: + +```tsx +type MyProps = { + // using `interface` is also ok + message: string; +}; +type MyState = { + count: number; // like this +}; +class App extends React.Component { + state: MyState = { + // optional second annotation for better type inference + count: 0, + }; + render() { + return ( +

    + {this.props.message} {this.state.count} +
    + ); + } +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) + +Don't forget that you can export/import/extend these types/interfaces for reuse. + +
    +Why annotate `state` twice? + +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. + +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. + +[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). + +
    + +
    + No need for readonly + +You often see sample code include `readonly` to mark props and state immutable: + +```tsx +type MyProps = { + readonly message: string; +}; +type MyState = { + readonly count: number; +}; +``` + +This is not necessary as `React.Component` already marks them as immutable. ([See PR and discussion!](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813)) + +
    + +**Class Methods**: Do it like normal, but just remember any arguments for your functions also need to be typed: + +```tsx +class App extends React.Component<{ message: string }, { count: number }> { + state = { count: 0 }; + render() { + return ( +
    this.increment(1)}> + {this.props.message} {this.state.count} +
    + ); + } + increment = (amt: number) => { + // like this + this.setState((state) => ({ + count: state.count + amt, + })); + }; +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) + +**Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: + +```tsx +class App extends React.Component<{ + message: string; +}> { + pointer: number; // like this + componentDidMount() { + this.pointer = 3; + } + render() { + return ( +
    + {this.props.message} and {this.pointer} +
    + ); + } +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/concurrent.md b/docs/basic/getting-started/concurrent.md new file mode 100644 index 00000000..6a2105fd --- /dev/null +++ b/docs/basic/getting-started/concurrent.md @@ -0,0 +1,8 @@ +--- +id: concurrent +title: Concurrent React/React Suspense +--- + +_Not written yet._ watch for more on React Suspense and Time Slicing. + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/context.md b/docs/basic/getting-started/context.md new file mode 100644 index 00000000..57292287 --- /dev/null +++ b/docs/basic/getting-started/context.md @@ -0,0 +1,237 @@ +--- +id: context +title: Context +--- + +Using `React.createContext` with an empty object as default value. + +```tsx +interface ContextState { + // set the type of state you want to handle with context e.g. + name: string | null; +} +//set an empty object as default state +const Context = React.createContext({} as ContextState); +// set up context provider as you normally would in JavaScript [React Context API](https://reactjs.org/docs/context.html#api) +``` + +Using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/) to make a `createCtx` with **no `defaultValue`, yet no need to check for `undefined`**: + +```ts +import * as React from "react"; + +const currentUserContext = React.createContext(undefined); + +function EnthusasticGreeting() { + const currentUser = React.useContext(currentUserContext); + return
    HELLO {currentUser!.toUpperCase()}!
    ; +} + +function App() { + return ( + + + + ); +} +``` + +Notice the explicit type arguments which we need because we don't have a default `string` value: + +```ts +const currentUserContext = React.createContext(undefined); +// ^^^^^^^^^^^^^^^^^^ +``` + +along with the non-null assertion to tell TypeScript that `currentUser` is definitely going to be there: + +```ts +return
    HELLO {currentUser!.toUpperCase()}!
    ; +// ^ +``` + +This is unfortunate because _we know_ that later in our app, a `Provider` is going to fill in the context. + +There are a few solutions for this: + +1. You can get around this by asserting non null: + + ```ts + const currentUserContext = React.createContext(undefined!); + ``` + + ([Playground here](https://www.typescriptlang.org/play/index.html?jsx=1#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQduEAdqvLgK5SXMwCqqLFADCLGFgAe8ALyYqMAHS5KycaN6SYAHjZRgzAOYA+ABQdmAEywF9WCwEIAlPQLn8wFnACivABYdUNBhgXABxSixgwxNHOABvOjg4JlZ2Lh5+QSg4WWw8RQCsdXEpE05uLF4BIWLNZ0S4ShguZjgtC2AANyMACS8AGX6AeXjyjOqoBRgIPjAwGrQsGIBfey0Aeg7u+mW6V2Z3TwBBOZj4hqaWtrHKzJqxTQUABWJO4CtszuQAGw4saTIAGVfMgAO7MMhGBpJLQ+GD+QJsELhLCRfQGODrKEw9Y3KpZWpSZ6vd5CIw7IA)) This is a quick and easy fix, but this loses type-safety, and if you forget to supply a value to the Provider, you will get an error. + +2. We can write a helper function called `createCtx` that guards against accessing a `Context` whose value wasn't provided. By doing this, API instead, **we never have to provide a default and never have to check for `undefined`**: + + ```tsx + import * as React from "react"; + + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) + throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } + + // Usage: + + // We still have to specify a type, but no default! + export const [useCurrentUserName, CurrentUserProvider] = createCtx(); + + function EnthusasticGreeting() { + const currentUser = useCurrentUserName(); + return
    HELLO {currentUser.toUpperCase()}!
    ; + } + + function App() { + return ( + + + + ); + } + ``` + + [View in the TypeScript Playground](http://www.typescriptlang.org/play/index.html?jsx=1&ssl=1&ssc=1&pln=31&pc=2#code/JYWwDg9gTgLgBAKjgQwM5wEoFNkGN4BmUEIcARFDvmQNwBQdA9AgnYnAIJwAWWANmCxQ4MCHFyVkMLCjgBhCADtpAD3jJFAEzgAFYgDdgmoXADuwGNziKxAVzBEl8YwWS2+8fcj62sAGhQtNiRzSwhbeG5kQ0UAcxExXF5cAGs4Amg4Wy0sAmBFLG1vPhFeEVAsADpgxjoCbPxgJXFJaTkYFQAeLiw1LC10AG8AXzgAH2t3PgA+AAoASjhBtnElVHh8FTgAXkwqGEqJHDanXphu8aycvILNOeyXfML5+jh0hpgmxSzULHaVBZLFZvXBrDY7PZ4A62X4KZRnWabF7AuDAAhwRE7ba7B65J6aRaWYimaxYEkAUSgxCgszIML+HTgIBh8AARjJ8qgjDJkLoDNzhKErLyvD4sGRkW83pQYLYoN9cK84MMVjK5d8ANr0-4BTaVPQQQzGKAAXRQ6FBinWNDgjEYcAA5GhVlaYA6mcgUlh0AAVACeggAyhJgGB4PkCCZebKwHwsHQVUx7QBVVDIWJYABcDDtcAA6jJ1sA+CUovoZKI4KhBLg0X7ZDAA-44KyItYxC43B4AIR0XqQWAu9ZwLWwuWUZSpoQAOWQIGbcnH-RgU6gBqNQjNuyOUgZXXWUHysTmyLqHy+cHJym4MLQn1wAHFKFhPnFAcsQWDxEvJ79hDixypZdV1necFiVNV5TgTpNGAfRpgACXJAAZZCAHkllwH8Vz-SpRGTMBBCgOQ0CwBZhm7TpGFg+D6ETepFEaZoOEI99VRfdVoMXIDfyEdcBTgUVfG2MhAyiUxFDIaYUU6K9LFvItH2fV94kYaS3io7iJxwvj+WNaY6KAA) + +3. You can go even further and combine this idea using `React.createContext` and [context getters](https://kentcdodds.com/blog/application-state-management-with-react/). + + ```tsx + /** + * A helper to create a Context and Provider with no upfront default value, and + * without having to check for undefined all the time. + */ + function createCtx
    () { + const ctx = React.createContext(undefined); + function useCtx() { + const c = React.useContext(ctx); + if (c === undefined) + throw new Error("useCtx must be inside a Provider with a value"); + return c; + } + return [useCtx, ctx.Provider] as const; // 'as const' makes TypeScript infer a tuple + } + + // usage + + export const [useCtx, SettingProvider] = createCtx(); // specify type, but no need to specify value upfront! + export function App() { + const key = useCustomHook("key"); // get a value from a hook, must be in a component + return ( + + + + ); + } + export function Component() { + const key = useCtx(); // can still use without null check! + return
    {key}
    ; + } + ``` + + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtCAOwGd4BXOpAYWZlwAkIIBrOAF44ACj5IAngC44DKMBoBzAJRCAfHADeFOHGr14AbQYoYSADSykMAMoxTSALpDExGADpmSOw5GaAvso6cEQwjFA0svZmhuISjhT+FAD0yXpEDnq0ZgAe8ADuwDAAFnA0EHCMYNjZcAAmSJgojAA2MABqKC2MSClphSUQjPDFKABuCopwnPUVjDQNmApIdXrFSGgCXS3T69OgveSY8xjAtOmoZqwwOQA8AIJqIqra5Lr6DHo3LsjoHmgZK7ZJB5B5wAA+lQWjWWdSe80WsOUAG5gscaKdzl5rjlnlpgu9aJ80D83J4WKxgXkRBgciiCXBgJhRABCNCqEo4fJlJDcgCiUBwUBEACJsd8QBw4AAjJCM+jABpwFBwAAKOAmDSgcAGpRVYy6PRF9LeuhC1nCkTQqNNSVNoUtcEM4pyllp7nVEE1SCgzhQdCyBmRcFScBAKHEcAAKhIwN4AcAwPAFJgfcrplUWhYyhB4ChIihBSgJHAIMz5mdIjBY0g6IkKH1KnQUIpDhQQZBYIHPs6KTdLDZrDBJp7vb6XADLmwbrc5JMniiQ2k6HG0EyS9W45ZpcMczyVtMKiuNuu4AbunKqjUaDAWe2cp2sCdh+d7mAwHjXoSDHA4i5sRw3C8HwopxMawahq2eZnoaco1HgKrFMBliSp8sryum1DgLQSA3sEDoRKIDK3IOMDDkoo6Kmm549IImhxP4agMrotyUthNC4fAyRMaaLHJKR5GKJRWo8boJp2h20BPhiL6RGxkAcTen7BB88B-sILrPBBaRoPmUTAC0OxeDqRRIbuNCtDsaDrJsd72hahG3HUwBjGo9GSP4tzJM5rk2v4QA) + +4. Using `React.createContext` and `useContext` to make a `createCtx` with [`unstated`](https://github.com/jamiebuilds/unstated)-like context setters: + + ```tsx + export function createCtx
    (defaultValue: A) { + type UpdateType = React.Dispatch< + React.SetStateAction + >; + const defaultUpdate: UpdateType = () => defaultValue; + const ctx = React.createContext({ + state: defaultValue, + update: defaultUpdate, + }); + function Provider(props: React.PropsWithChildren<{}>) { + const [state, update] = React.useState(defaultValue); + return ; + } + return [ctx, Provider] as const; // alternatively, [typeof ctx, typeof Provider] + } + + // usage + + const [ctx, TextProvider] = createCtx("someText"); + export const TextContext = ctx; + export function App() { + return ( + + + + ); + } + export function Component() { + const { state, update } = React.useContext(TextContext); + return ( + + ); + } + ``` + + [View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuNIlGJAYRjUAPAEEAfAAoAJkkwpGAGxgA1FIsZIAXHFEBKOAG8KcODACeYJHACqYabyQAVS9YC8iYjAB0AEWAAzmC8aAAWwsjoPgDKSDDRMI6ibBzCFlYQmHCy8kqq6pri4gDcJlwcAfA5Csp2Dnw6dY4uVnAekgZu4tlyNfkaSKXkpmgV8BjUbZ5R3tyofPwcfNQwksbDpnCVjjrVeWoDADRlpoz2Oz25ted8ZQC+ekOmTKww7JwACjgAbsCyUJIwDgwAEdJEMN4vhAQQB1YAwUL8ULARTSIjMYSGO7iAzrTblZiVOAAbW2fEOcDO9SQAF0puCfIwAkgEo4ZL19gUkI8TnAiDBGFBOMIJpCfn8kFA4N8uW5DIYtolyZSbtY7ncjN4tUDoQENQB6Er3Mr8wWcYkTClQ37-OkoAIEyrFOD6-VwdR8IW8YDfJCKcwU4npJCZLhCCnB0PWiVQGkUO4UCiuykBFAAcyQifIo0J8At4bgThoMGjtqmc0cgmokgARAFcM5izWeeQaHRxmNC8XFsxlvAPBMhm3oFgWClOKIwGAOkYTXEzXBJLzhEWVqXJeJeaZhItwBwkL2XZuNtv9auS+L-sfTC2E63aCOGGO3hw4LvIMwD6tcWUc0SFWSSAUlSjhwBqHgMt4TICEsxaSOePZ9i2pimkKi7LooKAAEZ+te+JGIBd74XAwjAMwYCMPAwZuDWfY1nAHBIigzAZnK7jdCBfCSEg3iJFAGY+DKAx6AaeGnphOGKHht5AA) + +5. A [useReducer-based version](https://gist.github.com/sw-yx/f18fe6dd4c43fddb3a4971e80114a052) may also be helpful. + +
    + +Mutable Context Using a Class component wrapper + +_Contributed by: [@jpavon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/13)_ + +```tsx +interface ProviderState { + themeColor: string; +} + +interface UpdateStateArg { + key: keyof ProviderState; + value: string; +} + +interface ProviderStore { + state: ProviderState; + update: (arg: UpdateStateArg) => void; +} + +const Context = React.createContext({} as ProviderStore); // type assertion on empty object + +class Provider extends React.Component<{}, ProviderState> { + public readonly state = { + themeColor: "red", + }; + + private update = ({ key, value }: UpdateStateArg) => { + this.setState({ [key]: value }); + }; + + public render() { + const store: ProviderStore = { + state: this.state, + update: this.update, + }; + + return ( + {this.props.children} + ); + } +} + +const Consumer = Context.Consumer; +``` + +
    + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/default-props.md b/docs/basic/getting-started/default-props.md new file mode 100644 index 00000000..7699e7fd --- /dev/null +++ b/docs/basic/getting-started/default-props.md @@ -0,0 +1,94 @@ +--- +id: default_props +title: Typing defaultProps +--- + +## Typing defaultProps + +For Typescript 3.0+, type inference [should work](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html), although [some edge cases are still problematic](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/61). Just type your props like normal, except don't use `React.FC`. + +```tsx +// //////////////// +// function components +// //////////////// +type Props = { age: number } & typeof defaultProps; +const defaultProps = { + who: "Johny Five", +}; + +const Greet = (props: Props) => { + /*...*/ +}; +Greet.defaultProps = defaultProps; +``` + +For **Class components**, there are [a couple ways to do it](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/103#issuecomment-481061483)(including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: + +```tsx +type GreetProps = typeof Greet.defaultProps & { + age: number; +}; + +class Greet extends React.Component { + static defaultProps = { + name: "world", + }; + /*...*/ +} + +// Type-checks! No type assertions needed! +let el = ; +``` + +
    + Why does React.FC break defaultProps? + +You can check the discussions here: + +- https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 +- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 +- https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87 + +This is just the current state and may be fixed in future. + +
    + +
    + Typescript 2.9 and earlier + +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: + +```ts +type Props = Required & { + /* additional props here */ +}; + +export class MyComponent extends React.Component { + static defaultProps = { + foo: "foo", + }; +} +``` + +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! + +```ts +interface IMyComponentProps { + firstProp?: string; + secondProp: IPerson[]; +} + +export class MyComponent extends React.Component { + public static defaultProps: Partial = { + firstProp: "default", + }; +} +``` + +The problem with this approach is it causes complex issues with the type inference working with `JSX.LibraryManagedAttributes`. Basically it causes the compiler to think that when creating a JSX expression with that component, that all of its props are optional. + +[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). + +
    + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/error-boundaries.md b/docs/basic/getting-started/error-boundaries.md new file mode 100644 index 00000000..57249249 --- /dev/null +++ b/docs/basic/getting-started/error-boundaries.md @@ -0,0 +1,8 @@ +--- +id: error_boundaries +title: Error Boundaries +--- + +_Not written yet._ + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/forms-and-events.md b/docs/basic/getting-started/forms-and-events.md new file mode 100644 index 00000000..97cacbc3 --- /dev/null +++ b/docs/basic/getting-started/forms-and-events.md @@ -0,0 +1,103 @@ +--- +id: forms_and_events +title: Forms and Events +--- + +If performance is not an issue, 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): + +```tsx +const el = ( + +)); +``` + +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). + +More info: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 + +You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/167). + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/function-components.md b/docs/basic/getting-started/function-components.md new file mode 100644 index 00000000..22192b70 --- /dev/null +++ b/docs/basic/getting-started/function-components.md @@ -0,0 +1,78 @@ +--- +id: function_components +title: 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 */ +const App = ({ message }: AppProps) =>
    {message}
    ; +``` + +
    + +What about `React.FC`/`React.FunctionComponent`? + +You can also write components with `React.FunctionComponent` (or the shorthand `React.FC` - they are the same): + +```tsx +const App: React.FunctionComponent<{ message: string }> = ({ message }) => ( +
    {message}
    +); +``` + +Some differences from the "normal function" version: + +- `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). + +- It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps`. + + - Note that there are some known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). We maintain a separate `defaultProps` section you can also look up. + +- It provides an implicit definition of `children` (see below) - however there are some issues with the implicit `children` type (e.g. [DefinitelyTyped#33006](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006)), and it might considered better style to be explicit about components that consume `children`, anyway. + +```tsx +const Title: React.FunctionComponent<{ title: string }> = ({ + children, + title, +}) =>
    {children}
    ; +``` + +- _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. + +In most cases it makes very little difference which syntax is used, but you may prefer the more explicit nature of `React.FunctionComponent`. + +
    + +
    +Minor Pitfalls + +These patterns are not supported: + +**Conditional rendering** + +```tsx +const MyConditionalComponent = ({ shouldRender = false }) => + shouldRender ?
    : false; // don't do this in JS either +const el = ; // throws an error +``` + +This is because due to limitations in the compiler, function components cannot return anything other than a JSX expression or `null`, otherwise it complains with a cryptic error message saying that the other type is not assignable to `Element`. + +```tsx +const MyArrayComponent = () => Array(5).fill(
    ); +const el2 = ; // throws an error +``` + +**Array.fill** + +Unfortunately just annotating the function type will not help so if you really need to return other exotic types that React supports, you'd need to perform a type assertion: + +```tsx +const MyArrayComponent = () => (Array(5).fill(
    ) as any) as JSX.Element; +``` + +[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). + +
    diff --git a/docs/basic/getting-started/get-derived-state-from-props.md b/docs/basic/getting-started/get-derived-state-from-props.md new file mode 100644 index 00000000..249bdefa --- /dev/null +++ b/docs/basic/getting-started/get-derived-state-from-props.md @@ -0,0 +1,66 @@ +--- +id: get_derived_props_from_state +title: getDerivedStateFromProps +--- + +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 easily achieved using hooks which can also help set up memoization easily. + +Here are a few ways in which you can annotate `getDerivedStateFromProps` + +1. If you have explicitly typed your derived state and want to make sure that the return value from `getDerivedStateFromProps` conforms to it. + +```tsx +class Comp extends React.Component { + static getDerivedStateFromProps( + props: Props, + state: State + ): Partial | null { + // + } +} +``` + +2. When you want the function's return value to determine your state. + +```tsx +class Comp extends React.Component< + Props, + ReturnType +> { + static getDerivedStateFromProps(props: Props) {} +} +``` + +3. When you want derived state with other state fields and memoization + +```tsx +type CustomValue = any; +interface Props { + propA: CustomValue; +} +interface DefinedState { + otherStateField: string; +} +type State = DefinedState & ReturnType; +function transformPropsToState(props: Props) { + return { + savedPropA: props.propA, // save for memoization + derivedState: props.propA, + }; +} +class Comp extends React.PureComponent { + constructor(props: Props) { + super(props); + this.state = { + otherStateField: "123", + ...transformPropsToState(props), + }; + } + static getDerivedStateFromProps(props: Props, state: State) { + if (isEqual(props.propA, state.savedPropA)) return null; + return transformPropsToState(props); + } +} +``` + +[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) diff --git a/docs/basic/getting-started/hooks.md b/docs/basic/getting-started/hooks.md new file mode 100644 index 00000000..d6cf2a26 --- /dev/null +++ b/docs/basic/getting-started/hooks.md @@ -0,0 +1,192 @@ +--- +id: hooks +title: 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: + +```tsx +const [val, toggle] = React.useState(false); // `val` is inferred to be a boolean, `toggle` only takes booleans +``` + +See also the [Using Inferred Types](#using-inferred-types) section if you need to use a complex type that you've relied on inference for. + +However, many hooks are initialized with null-ish default values, and you may wonder how to provide types. Explicitly declare the type, and use a union type: + +```tsx +const [user, setUser] = React.useState(null); + +// later... +setUser(newUser); +``` + +**useRef** + +When using `useRef`, you have two options when creating a ref container that does not have an initial value: + +```ts +const ref1 = useRef(null!); +const ref2 = useRef(null); +``` + +The first option will make `ref1.current` read-only, 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). + +The second option will make `ref2.current` mutable, and is intended for "instance variables" that you manage yourself. + +**useEffect** + +When using `useEffect`, take care not to return anything other than a function or `undefined`, otherwise both TypeScript and React will yell at you. This can be subtle when using arrow functions: + +```ts +function DelayedEffect(props: { timerMs: number }) { + const { timerMs } = props; + // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces + useEffect( + () => + setTimeout(() => { + /* do stuff */ + }, timerMs), + [timerMs] + ); + return null; +} +``` + +**useRef** + +```tsx +function TextInputWithFocusButton() { + // initialise with null, but tell TypeScript we are looking for an HTMLInputElement + const inputEl = React.useRef(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! */} + + + + ); +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) + +example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) + +**useReducer** + +You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. + +```tsx +type AppState = {}; +type Action = + | { type: "SET_ONE"; payload: string } + | { type: "SET_TWO"; payload: number }; + +export function reducer(state: AppState, action: Action): AppState { + switch (action.type) { + case "SET_ONE": + return { + ...state, + one: action.payload, // `payload` is string + }; + case "SET_TWO": + return { + ...state, + two: action.payload, // `payload` is number + }; + default: + return state; + } +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) + +**Custom Hooks** + +If you are returning an array in your Custom Hook, you will want to avoid type inference as Typescript will infer a union type (when you actually want different types in each position of the array). Instead, use [TS 3.4 const assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions): + +```tsx +export function useLoading() { + const [isLoading, setState] = React.useState(false); + const load = (aPromise: Promise) => { + setState(true); + return aPromise.finally(() => setState(false)); + }; + return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) + +This way, when you destructure you actually get the right types based on destructure position. + +
    +Alternative: Asserting a tuple return type + +If you are [having trouble with const assertions](https://github.com/babel/babel/issues/9800), you can also assert or define the function return types: + +```tsx +export function useLoading() { + const [isLoading, setState] = React.useState(false); + const load = (aPromise: Promise) => { + setState(true); + return aPromise.finally(() => setState(false)); + }; + return [isLoading, load] as [ + boolean, + (aPromise: Promise) => Promise + ]; +} +``` + +A helper function that automatically types tuples can also be helpful if you write a lot of custom hooks: + +```ts +function tuplify(...elements: T) { + return elements; +} + +function useArray() { + const numberValue = useRef(3).current; + const functionValue = useRef(() => {}).current; + return [numberValue, functionValue]; // type is (number | (() => void))[] +} + +function useTuple() { + const numberValue = useRef(3).current; + const functionValue = useRef(() => {}).current; + return tuplify(numberValue, functionValue); // type is [number, () => void] +} +``` + +
    + +Note that the React team recommends that custom hooks that return more than two values should use proper objects instead of tuples, however. + +More Hooks + TypeScript reading: + +- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d +- https://fettblog.eu/typescript-react/hooks/#useref + +If you are writing a React Hooks library, don't forget that you should also expose your types for users to use. + +Example React Hooks + TypeScript Libraries: + +- https://github.com/mweststrate/use-st8 +- https://github.com/palmerhq/the-platform +- https://github.com/sw-yx/hooks + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/portals.md b/docs/basic/getting-started/portals.md new file mode 100644 index 00000000..5010bd33 --- /dev/null +++ b/docs/basic/getting-started/portals.md @@ -0,0 +1,37 @@ +--- +id: portals +title: Portals +--- + +Using `ReactDOM.createPortal`: + +```tsx +const modalRoot = document.getElementById("modal-root") as HTMLElement; +// assuming in your html file has a div with id 'modal-root'; + +export class Modal extends React.Component { + el: HTMLElement = document.createElement("div"); + + componentDidMount() { + modalRoot.appendChild(this.el); + } + + componentWillUnmount() { + modalRoot.removeChild(this.el); + } + + render() { + return ReactDOM.createPortal(this.props.children, this.el); + } +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWRYmAEQHkBZObXAo9GAWgBNcZchTQQAdgGd4ICHxQAbBBAjwAvHAFoAriCRiYAOgDmSGAFF5SXfoBCATwCSfABQAiGXPk8cK1wEo4FAk4AAkAFWYAGQsrPRgAbgoAeiTAiQkdYDEjOCy4OwgtKDgACxgQeTZgS1KgwI1gADc4AHdgGBLcvgIPBW9lGHxE4XIkAA9qeDR5IODmWQU4cZg9PmDkbgMAYVxIMTi4AG8KOCX5AC5QiOjLazUNCG07gzQuFZi7tz4m-2GTuFE4HEcXowD48y0+mcAWO5FOp16igGBhQYDAqy2JWqLg6wAkBiQ8j8w1OAF8KP9AXs4gB1aryACqYhkkJg0KO-wRCyRKgMRBkjSQmOxzlx+MJxP+5JGpyIYj4SCg7Nh8LgRBgRTEtG4TGYLzeSAACtAYApRVj8WAcGB8WgsfI+HKADRwMUEokkuDS0lAA) + +
    + +Context of Example + +This example is based on the [Event Bubbling Through Portal](https://reactjs.org/docs/portals.html#event-bubbling-through-portals) example of React docs. + +
    diff --git a/docs/basic/getting-started/react-prop-type-examples.md b/docs/basic/getting-started/react-prop-type-examples.md new file mode 100644 index 00000000..b7b4c13e --- /dev/null +++ b/docs/basic/getting-started/react-prop-type-examples.md @@ -0,0 +1,31 @@ +--- +id: react_prop_type_example +title: Useful React Prop Type Examples +--- + +```tsx +export declare interface AppProps { + children1: JSX.Element; // bad, doesnt account for arrays + children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings + children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility + children4: React.ReactChild[]; // better + children: React.ReactNode; // best, accepts everything + functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type + style?: React.CSSProperties; // to pass through style props + onChange?: React.FormEventHandler; // form events! the generic parameter is the type of event.target + props: Props & React.PropsWithoutRef; // to impersonate all the props of a button element without its ref +} +``` + +
    + JSX.Element vs React.ReactNode? + +Quote [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57): A more technical explanation is that a valid React node is not the same thing as what is returned by `React.createElement`. Regardless of what a component ends up rendering, `React.createElement` always returns an object, which is the `JSX.Element` interface, but `React.ReactNode` is the set of all possible return values of a component. + +- `JSX.Element` -> Return value of `React.createElement` +- `React.ReactNode` -> Return value of a component +
    + +[More discussion: Where ReactNode does not overlap with JSX.Element](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/129) + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/getting-started/type-or-inteface.md b/docs/basic/getting-started/type-or-inteface.md new file mode 100644 index 00000000..3f0c74e3 --- /dev/null +++ b/docs/basic/getting-started/type-or-inteface.md @@ -0,0 +1,24 @@ +--- +id: types_or_interfaces +title: Types or Interfaces? +--- + +`interface`s are different from `type`s in TypeScript, but they can be used for very similar things as far as common React uses cases are concerned. Here's a helpful rule of thumb: + +- always use `interface` for public API's definition when authoring a library or 3rd party ambient type definitions. + +- consider using `type` for your React Component Props and State, because it is more constrained. + +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. + +
    + + Useful table for Types vs Interfaces + +It's a nuanced topic, don't get too hung up on it. Here's a handy graphic: + +![https://pbs.twimg.com/media/DwV-oOsXcAIct2q.jpg](https://pbs.twimg.com/media/DwV-oOsXcAIct2q.jpg) (source: [Karol Majewski](https://twitter.com/karoljmajewski/status/1082413696075382785)) + +
    + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/linting.md b/docs/basic/linting.md new file mode 100644 index 00000000..38314df0 --- /dev/null +++ b/docs/basic/linting.md @@ -0,0 +1,105 @@ +--- +id: linting +title: 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). + +> ⚠️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. + +Follow the TypeScript + ESLint docs at https://github.com/typescript-eslint/typescript-eslint: + +``` +yarn add -D @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint +``` + +add a `lint` script to your `package.json`: + +```json + "scripts": { + "lint": "eslint 'src/**/*.ts'" + }, +``` + +and a suitable `.eslintrc.js` (using `.js` over `.json` here so we can add comments): + +```js +module.exports = { + env: { + es6: true, + node: true, + jest: true, + }, + extends: "eslint:recommended", + parser: "@typescript-eslint/parser", + plugins: ["@typescript-eslint"], + parserOptions: { + ecmaVersion: 2017, + sourceType: "module", + }, + rules: { + indent: ["error", 2], + "linebreak-style": ["error", "unix"], + quotes: ["error", "single"], + "no-console": "warn", + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": [ + "error", + { vars: "all", args: "after-used", ignoreRestSiblings: false }, + ], + "@typescript-eslint/explicit-function-return-type": "warn", // Consider using explicit annotations for object literals and function return types even when they can be inferred. + "no-empty": "warn", + }, +}; +``` + +Most of this is taken from [the `tsdx` PR](https://github.com/palmerhq/tsdx/pull/70/files) which is for **libraries**. + +More `.eslintrc.json` options to consider with more options you may want for **apps**: + +```json +{ + "extends": [ + "airbnb", + "prettier", + "prettier/react", + "plugin:prettier/recommended", + "plugin:jest/recommended", + "plugin:unicorn/recommended" + ], + "plugins": ["prettier", "jest", "unicorn"], + "parserOptions": { + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "env": { + "es6": true, + "browser": true, + "jest": true + }, + "settings": { + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + }, + "overrides": [ + { + "files": ["**/*.ts", "**/*.tsx"], + "parser": "typescript-eslint-parser", + "rules": { + "no-undef": "off" + } + } + ] +} +``` + +You can read a [fuller TypeScript + ESLint setup guide here](https://blog.matterhorn.dev/posts/learn-typescript-linting-part-1/) from Matterhorn, in particular check https://github.com/MatterhornDev/learn-typescript-linting. + +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. + +If you're looking for information on Prettier, check out the [Prettier](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md#prettier). diff --git a/docs/basic/recommended/other-resources.md b/docs/basic/recommended/other-resources.md new file mode 100644 index 00000000..7afd60bf --- /dev/null +++ b/docs/basic/recommended/other-resources.md @@ -0,0 +1,19 @@ +--- +id: other_resources +title: Other React + TypeScript resources +sidebar_label: Other resources +--- + +- me! +- - **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**. +- [Ultimate React Component Patterns with TypeScript 2.8](https://levelup.gitconnected.com/ultimate-react-component-patterns-with-typescript-2-8-82990c516935) +- [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. +- [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) +- [Sindre Sorhus' TypeScript Style Guide](https://github.com/sindresorhus/typescript-definition-style-guide) +- [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 doesnt seem to be frequently updated anymore. +- [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 +- Typescript conversion: + - [Lyft's React-To-Typescript conversion CLI](https://github.com/lyft/react-javascript-to-typescript-transform) + - [Gustav Wengel's blogpost - converting a React codebase to Typescript](http://www.gustavwengel.dk/converting-typescript-to-javascript-part-1) + - [Microsoft React Typescript conversion guide](https://github.com/Microsoft/TypeScript-React-Conversion-Guide#typescript-react-conversion-guide) +- [You?](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/docs/basic/recommended/resources.md b/docs/basic/recommended/resources.md new file mode 100644 index 00000000..bf97e687 --- /dev/null +++ b/docs/basic/recommended/resources.md @@ -0,0 +1,29 @@ +--- +id: resources +title: Recommended React + TypeScript codebases to learn from +sidebar_label: Codebases to learn from +--- + +- https://github.com/jaredpalmer/formik +- https://github.com/jaredpalmer/react-fns +- https://github.com/palantir/blueprint +- https://github.com/Shopify/polaris +- https://github.com/NullVoxPopuli/react-vs-ember/tree/master/testing/react +- https://github.com/artsy/reaction +- https://github.com/benawad/codeponder (with [coding livestream!](https://www.youtube.com/watch?v=D8IJOwdNSkc&list=PLN3n1USn4xlnI6kwzI8WrNgSdG4Z6daCq)) +- https://github.com/artsy/emission (React Native) +- [@reach/ui's community typings](https://github.com/reach/reach-ui/pull/105) + +React Boilerplates: + +- https://github.com/rwieruch/nextjs-firebase-authentication: Next.js + Firebase Starter: styled, tested, typed, and authenticated +- [@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) +- [webpack config tool](https://webpack.jakoblind.no/) is a visual tool for creating webpack projects with React and TypeScript +- ready to go template with [Material-UI](https://material-ui.com/), routing and Redux + +React Native Boilerplates: _contributed by [@spoeck](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/20)_ + +- https://github.com/GeekyAnts/react-native-seed +- https://github.com/lopezjurip/ReactNativeTS +- https://github.com/emin93/react-native-template-typescript +- diff --git a/docs/basic/recommended/talks.md b/docs/basic/recommended/talks.md new file mode 100644 index 00000000..2cdce871 --- /dev/null +++ b/docs/basic/recommended/talks.md @@ -0,0 +1,7 @@ +--- +id: talks +title: Recommended React + TypeScript talks +sidebar_label: Talks +--- + +- Please help contribute this new section! diff --git a/docs/basic/setup.md b/docs/basic/setup.md new file mode 100644 index 00000000..596bba53 --- /dev/null +++ b/docs/basic/setup.md @@ -0,0 +1,48 @@ +--- +id: setup +title: Setup +--- + +## Prerequisites + +1. good understanding of [React](https://reactjs.org) +2. familiarity with [TypeScript Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful. If you’re an absolute beginner in TypeScript, check out [chibicode’s tutorial](https://ts.chibicode.com/todo/).) +3. having read [the TypeScript section in the official React docs](https://reactjs.org/docs/static-type-checking.html#typescript). +4. having read [the React section of the new TypeScript playground](http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react) (optional: also step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section) + +This guide will always assume you are starting with the latest TypeScript version. Notes for older versions will be in expandable `
    ` tags. + +## React + TypeScript Starter Kits + +1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app my-app --template typescript` + +- We used to recommend `create-react-app-typescript` but it is now [deprecated](https://www.reddit.com/r/reactjs/comments/a5919a/createreactapptypescript_has_been_archived_rip/). [see migration instructions](https://vincenttunru.com/migrate-create-react-app-typescript-to-create-react-app/) + +2. [Basarat's guide](https://github.com/basarat/typescript-react/tree/master/01%20bootstrap) for **manual setup** of React + TypeScript + Webpack + Babel + +- In particular, make sure that you have `@types/react` and `@types/react-dom` installed ([Read more about the DefinitelyTyped project if you are unfamiliar](https://definitelytyped.org/)) +- There are also many React + TypeScript boilerplates, please see [our Resources list below](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#recommended-react--typescript-codebases-to-learn-from). + +## Import React + +```tsx +import * as React from "react"; +import * as ReactDOM from "react-dom"; +``` + +In [TypeScript 2.7+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html), you can run TypeScript with `--allowSyntheticDefaultImports` (or add `"allowSyntheticDefaultImports": true` to tsconfig) to import like in regular jsx: + +```tsx +import React from "react"; +import ReactDOM from "react-dom"; +``` + +
    + +Explanation + +Why `allowSyntheticDefaultImports` over `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) has said that it's better for webpack/parcel. For more discussion check out + +You should also check [the new TypeScript docs for official descriptions between each compiler flag](https://www.typescriptlang.org/v2/en/tsconfig#allowSyntheticDefaultImports)! + +
    diff --git a/docs/basic/troubleshooting/learn-ts.md b/docs/basic/troubleshooting/learn-ts.md new file mode 100644 index 00000000..8e9e23ad --- /dev/null +++ b/docs/basic/troubleshooting/learn-ts.md @@ -0,0 +1,16 @@ +--- +id: learn_ts +title: Getting Started +sidebar_label: Time to Really Learn TypeScript +--- + +Believe it or not, we have only barely introduced TypeScript here in this cheatsheet. 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 :) + +It is worth mentioning some resources to help you get started: + +- Step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section, written by @Orta +- 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/ +- 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`) +- Shu Uesugi: [TypeScript for Beginner Programmers](https://ts.chibicode.com/) diff --git a/docs/basic/troubleshooting/non-ts-files.md b/docs/basic/troubleshooting/non-ts-files.md new file mode 100644 index 00000000..126bfa96 --- /dev/null +++ b/docs/basic/troubleshooting/non-ts-files.md @@ -0,0 +1,20 @@ +--- +id: non_ts_files +title: Troubleshooting Handbook: Images and other non-TS/TSX files +sidebar_label: Images and other non-TS/TSX files +--- + +Use [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html): + +```ts +// declaration.d.ts +// anywhere in your project, NOT the same name as any of your .ts/tsx files +declare module "*.png"; + +// importing in a tsx file +import * as logo from "./logo.png"; +``` + +Note that `tsc` cannot bundle these files for you, you will have to use Webpack or Parcel. + +Related issue: https://github.com/Microsoft/TypeScript-React-Starter/issues/12 and [StackOverflow](https://stackoverflow.com/a/49715468/4216035) diff --git a/docs/basic/troubleshooting/official-typings-bugs.md b/docs/basic/troubleshooting/official-typings-bugs.md new file mode 100644 index 00000000..1f48a4bc --- /dev/null +++ b/docs/basic/troubleshooting/official-typings-bugs.md @@ -0,0 +1,66 @@ +--- +id: official_typings_bugs +title: Getting Started +sidebar_label: 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`: + +```json +{ + "compilerOptions": { + "paths": { + "mobx-react": ["../typings/modules/mobx-react"] + } + } +} +``` + +[Thanks to @adamrackis for the tip.](https://twitter.com/AdamRackis/status/1024827730452520963) + +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): + +```tsx +// my-typings.ts +declare module "plotly.js" { + interface PlotlyHTMLElement { + removeAllListeners(): void; + } +} + +// MyComponent.tsx +import { PlotlyHTMLElement } from "plotly.js"; + +const f = (e: PlotlyHTMLElement) => { + e.removeAllListeners(); +}; +``` + +You dont always have to implement the module, you can simply import the module as `any` for a quick start: + +```tsx +// my-typings.ts +declare module "plotly.js"; // each of its imports are `any` +``` + +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. + +You can also do ambient variable and ambient type declarations: + +```ts +// ambient utiltity type +type ToArray = T extends unknown[] ? T : T[]; +// ambient variable +declare let process: { + env: { + NODE_ENV: "development" | "production"; + }; +}; +process = { + env: { + NODE_ENV: "production", + }, +}; +``` + +You can see examples of these included in the built in type declarations in the `lib` field of `tsconfig.json` diff --git a/docs/basic/troubleshooting/operators.md b/docs/basic/troubleshooting/operators.md new file mode 100644 index 00000000..0c034da7 --- /dev/null +++ b/docs/basic/troubleshooting/operators.md @@ -0,0 +1,21 @@ +--- +id: operators +title: Troubleshooting Handbook: Operators +sidebar_label: Operators +--- + +- `typeof` and `instanceof`: type query used for refinement +- `keyof`: get keys of an object +- `O[K]`: property lookup +- `[K in O]`: mapped types +- `+` or `-` or `readonly` or `?`: addition and subtraction and readonly and optional modifiers +- `x ? Y : Z`: Conditional types for generic types, type aliases, function parameter types +- `!`: Nonnull assertion for nullable types +- `=`: Generic type parameter default for generic types +- `as`: type assertion +- `is`: type guard for function return types + +Conditional Types are a difficult topic to get around so here are some extra resources: + +- fully walked through explanation https://artsy.github.io/blog/2018/11/21/conditional-types-in-typescript/ +- Bailing out and other advanced topics https://github.com/sw-yx/ts-spec/blob/master/conditional-types.md diff --git a/docs/basic/troubleshooting/ts-config.md b/docs/basic/troubleshooting/ts-config.md new file mode 100644 index 00000000..c7c445b0 --- /dev/null +++ b/docs/basic/troubleshooting/ts-config.md @@ -0,0 +1,49 @@ +--- +id: tsconfig +title: Troubleshooting Handbook: tsconfig.json +sidebar_label: 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/v2/en/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`): + +```json +{ + "compilerOptions": { + "incremental": true, + "outDir": "build/lib", + "target": "es5", + "module": "esnext", + "lib": ["dom", "esnext"], + "sourceMap": true, + "importHelpers": true, + "declaration": true, + "rootDir": "src", + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "allowJs": false, + "jsx": "react", + "moduleResolution": "node", + "baseUrl": "src", + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "suppressImplicitAnyIndexErrors": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "build", "scripts"] +} +``` + +Please open an issue and discuss if there are better recommended choices for React. + +Selected flags and why we like them: + +- `esModuleInterop`: disables namespace imports (`import * as foo from "foo"`) and enables CJS/AMD/UMD style imports (`import fs from "fs"`) +- `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. +- `"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. + +Compilation speed 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](ADVANCED.md) cheatsheet for commentary. diff --git a/docs/basic/troubleshooting/types.md b/docs/basic/troubleshooting/types.md new file mode 100644 index 00000000..be923608 --- /dev/null +++ b/docs/basic/troubleshooting/types.md @@ -0,0 +1,369 @@ +--- +id: types +title: Troubleshooting Handbook: Types +sidebar_label: Types +--- + +> ⚠️ Have you read [the TypeScript FAQ](https://github.com/microsoft/TypeScript/wiki/FAQ)?) Your answer might be there! + +Facing weird type errors? You aren't alone. This is the hardest part of using TypeScript with React. Be patient - you are learning a new language after all. However, the more you get good at this, the less time you'll be working _against_ the compiler and the more the compiler will be working _for_ you! + +Try to avoid typing with `any` as much as possible to experience the full benefits of typescript. Instead, let's try to be familiar with some of the common strategies to solve these issues. + +## Union Types and Type Guarding + +Union types are handy for solving some of these typing problems: + +```tsx +class App extends React.Component< + {}, + { + count: number | null; // like this + } +> { + state = { + count: null, + }; + render() { + return
    this.increment(1)}>{this.state.count}
    ; + } + increment = (amt: number) => { + this.setState((state) => ({ + count: (state.count || 0) + amt, + })); + }; +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) + +**Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. If `A` and `B` are both object types, `A | B` isn't "either A or B", it is "A or B or both at once", which causes some confusion if you expected it to be the former. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: + +```ts +interface Admin { + role: string; +} +interface User { + email: string; +} + +// Method 1: use `in` keyword +function redirect(user: Admin | User) { + if ("role" in user) { + // use the `in` operator for typeguards since TS 2.7+ + routeToAdminPage(user.role); + } else { + routeToHomePage(user.email); + } +} + +// Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough +function isAdmin(user: Admin | User): user is Admin { + return (user as any).role !== undefined; +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) + +Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. + +If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) if you need help. (See also: [Basarat's writeup](https://basarat.gitbook.io/typescript/type-system/discriminated-unions)). This is handy in typing reducers for `useReducer` or Redux. + +## Optional Types + +If a component has an optional prop, add a question mark and assign during destructure (or use defaultProps). + +```tsx +class MyComponent extends React.Component<{ + message?: string; // like this +}> { + render() { + const { message = "default" } = this.props; + return
    {message}
    ; + } +} +``` + +You can also use a `!` character to assert that something is not undefined, but this is not encouraged. + +_Something to add? [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new) with your suggestions!_ + +## Enum Types + +Enums in TypeScript default to numbers. You will usually want to use them as strings instead: + +```tsx +export enum ButtonSizes { + default = "default", + small = "small", + large = "large", +} +``` + +Usage: + +```tsx +export const PrimaryButton = ( + props: Props & React.HTMLProps +) =>
    + +
    ` and `` - */ -export default Object.assign(Form, { Input: Input }); -``` - -[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) - -(Contributed by @bryceosterhaus, see [further discussion](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/165)) - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Design System Development - -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) - -For developing with Storybook, read the docs I wrote over here: . This includes automatic proptype documentation generation, which is awesome :) - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Migrating From Flow - -You should check out large projects that are migrating from flow to pick up concerns and tips: - -- [Jest](https://github.com/facebook/jest/pull/7554) -- [Expo](https://github.com/expo/expo/issues/2164) -- [React-beautiful-dnd](https://github.com/atlassian/react-beautiful-dnd/issues/982) -- [Storybook](https://github.com/storybooks/storybook/issues/5030) -- [VueJS](https://medium.com/the-vue-point/plans-for-the-next-iteration-of-vue-js-777ffea6fabf) - -Useful libraries: - -- -- -- - -If you have specific advice in this area, please file a PR! - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Prettier - -There isn't any real secret to Prettier for TypeScript. But its a great idea to run prettier on every commit! - -```bash -$ yarn add -D prettier husky lint-staged -``` - -```json -// inside package.json -{ - //... - "husky": { - "hooks": { - "pre-commit": "lint-staged" - } - }, - "lint-staged": { - "linters": { - "src/*.{ts,tsx,js,jsx,css,scss,md}": [ - "prettier --trailing-comma es5 --single-quote --write", - "git add" - ], - "ignore": ["**/dist/*, **/node_modules/*"] - } - }, - "prettier": { - "printWidth": 80, - "semi": false, - "singleQuote": true, - "trailingComma": "es5" - } -} -``` - -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) - -For library authors, this is set up for you in [tsdx](https://github.com/palmerhq/tsdx/pull/45/files). - -## Testing - -Yes, you can test your types! You shouldn't use it for EVERYTHING, but it can help prevent regressions: - -- https://github.com/azz/jest-runner-tsc -- https://github.com/SamVerschueren/tsd -- https://github.com/ikatyang/dts-jest ([Demo](https://codesandbox.io/s/dts-test-frozen-public-demo-iyorn)) -- https://github.com/microsoft/dtslint ([Intro to dtslint](https://www.youtube.com/watch?v=nygcFEwOG8w&feature=share)) - -## Working with Non-TypeScript Libraries (writing your own index.d.ts) - -Lets say you want to use `de-indent`, but it isn't typed or on DefinitelyTyped. You get an error like this: - -``` -[ts] -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. - Try `npm install @types/de-indent` if it exists or add a new declaration (.d.ts) file containing `declare module 'de-indent';` [7016] -``` - -So create a `.d.ts` file anywhere in your project with the module definition: - -```ts -// de-indent.d.ts -declare module "de-indent" { - function deindent(): void; - export = deindent; // default export -} -``` - -
    - -Further Discussion - -Any other tips? Please contribute on this topic! [We have an ongoing issue here with some references](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/8). We have more discussion and examples [in our issue here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/12). - -
    - -# Section 4: @types/react and @types/react-dom APIs - -The `@types` typings export both "public" types meant for your use as well as "private" types that are for internal use. - -Check [SaltyCrane's React TypeScript Cheatsheet](https://github.com/saltycrane/typescript-cheatsheet) for a nice autogenerated complete reference. - -## `@types/react` - -[Link to `.d.ts`](https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/react/index.d.ts) - -**Namespace: React** - -Most Commonly Used Interfaces and Types - -- `ReactNode` - anything that is renderable _inside_ of JSX, this is NOT the same as what can be rendered by a component! -- `Component` - base class of all class-based components -- `PureComponent` - base class for all class-based optimized components -- `FC`, `FunctionComponent` - a complete interface for function components, often used to type external components instead of typing your own -- `CSSProperties` - used to type style objects -- all events: used to type event handlers -- all event handlers: used to type event handlers -- all consts: `Children`, `Fragment`, ... are all public and reflect the React runtime namespace - -Not Commonly Used but Good to know - -- `Ref` - used to type `innerRef` -- `ElementType` - used for higher order components or operations on components -- `ComponentType` - used for higher order components where you don't specifically deal with the intrinsic components -- `ReactPortal` - used if you specifically need to type a prop as a portal, otherwise it is part of `ReactNode` -- `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 -- `JSXElementConstructor` - anything that TypeScript considers to be a valid thing that can go into the opening tag of a JSX expression -- `ComponentProps` - props of a component -- `ComponentPropsWithRef` - props of a component where if it is a class-based component it will replace the `ref` prop with its own instance type -- `ComponentPropsWithoutRef` - props of a component without its `ref` prop -- all methods: `createElement`, `cloneElement`, ... are all public and reflect the React runtime API - -[@Ferdaber's note](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69): I discourage the use of most `...Element` types because of how black-boxy `JSX.Element` is. You should almost always assume that anything produced by `React.createElement` is the base type `React.ReactElement`. - -**Namespace: JSX** - -- `Element` - the type of any JSX expression -- `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. -- `IntrinsicElements` - every possible built-in component that can be typed in as a lowercase tag name in JSX - -Not commonly used but good to know - -- `IntrinsicAttributes` set of attributes that all `IntrinsicElements` support... basically just `key`. -- `ElementChildrenAttribute` name of property that TS looks at to figure out what types of children a component supports. Basically the `children` property -- `ElementAttributesProperty` name of property that TS looks at to figure out what attributes a component supports. Basically the `props` property (for a class instance) - -**Don't use/Internal/Deprecated** - -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. - -- `SFCElement` -- `SFC` -- `ComponentState` -- `LegacyRef` -- `StatelessComponent` -- `ReactType` - -### 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 -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 -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`. - -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: - -```ts -// react-unstable-attributes.d.ts -import "react"; - -declare module "react" { - interface ImgHTMLAttributes extends HTMLAttributes { - loading?: "auto" | "eager" | "lazy"; - } -} -``` - -## `@types/react-dom` - -To be written - -# My question isn't answered here! - -- [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..c7f1856c --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:lts + +WORKDIR /app/website + +EXPOSE 3000 35729 +COPY ./docs /app/docs +COPY ./website /app/website +RUN yarn install + +CMD ["yarn", "start"] diff --git a/MIGRATING.md b/MIGRATING.md deleted file mode 100644 index f7db99a5..00000000 --- a/MIGRATING.md +++ /dev/null @@ -1,260 +0,0 @@ -
    - - - react + ts logo - - -

    Cheatsheets for experienced React developers getting started with TypeScript

    - -[**Basic**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | -[**Advanced**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md) | -[**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | -[**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | -[中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | -[**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | -[Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | -[Ask!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose) - -
    - ---- - -# Migrating (to TypeScript) Cheatsheet - -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. - -> ⚠️ This Cheatsheet is extremely new and could use all the help we can get. Solid advice, results, and up to date content all welcome. - -## Prerequsite - -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). - -## General Conversion approaches - -- Level 0: Don't use TypeScript, use JSDoc - - See our [JSDoc section](#JSDoc) -- Level 1A: Majority JavaScript, increasingly strict TypeScript - - as recommended by [official TS guide](https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html) - - use `allowJS` (Experiences: [clayallsop][clayallsop], [pleo][pleo]) -- Level 1B: Total rename to TypeScript from the start - - "[Just rename all .js files to .ts](https://twitter.com/jamonholmgren/status/1089241726303199232)"? - - use the loosest, bare minimum settings to start with -- Level 2: Strict TypeScript - - use Microsoft's [`dts-gen`](https://github.com/Microsoft/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. - - use `declare` keyword for ambient declarations - see [declaration merging](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#troubleshooting-handbook-bugs-in-official-typings) to patch library declarations inline - -Misc tips/approaches successful companies have taken - -- `@ts-ignore` on compiler errors for libraries with no typedefs -- 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). -- 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]) - -
    - - -Webpack tips - - - -- 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)) -- Webpack config: - -```js -module.exports = { - -resolve: { -- extensions: ['.js', '.jsx'] -+ extensions: ['.ts', '.tsx', '.js', '.jsx'] -}, - -// Source maps support ('inline-source-map' also works) -devtool: 'source-map', - -// Add the loader for .ts files. -module: { - loaders: [{ -- test: /\.jsx?$/, -- loader: 'babel-loader', -- exclude: [/node_modules/], -+ test: /\.(t|j)sx?$/, -+ loader: ['awesome-typescript-loader?module=es6'], -+ exclude: [/node_modules/] -+ }, { -+ test: /\.js$/, -+ loader: 'source-map-loader', -+ enforce: 'pre' - }] -} -}; -``` - -Special note on `ts-loader` and 3rd party libraries: https://twitter.com/acemarke/status/1091150384184229888 - -
    - -## JSDoc - -- https://github.com/Microsoft/TypeScript/wiki/JsDoc-support-in-JavaScript -- webpack's codebase uses JSDoc with linting by TS https://twitter.com/TheLarkInn/status/984479953927327744 (some crazy hack: https://twitter.com/thelarkinn/status/996475530944823296) - -Problems to be aware of: - -- `object` is converted to `any` for some reason. -- If you have an error in the jsdoc, you get no warning/error. TS just silently doesn't type annotate the function. -- [casting can be verbose](https://twitter.com/bahmutov/status/1089229349637754880) - -(_thanks [Gil Tayar](https://twitter.com/giltayar/status/1089228919260221441) and [Gleb Bahmutov](https://twitter.com/bahmutov/status/1089229196247908353) for sharing above commentary_) - -## From JS - -### Automated JS to TS Conversion - -- [TypeStat](https://github.com/JoshuaKGoldberg/TypeStat) ([used by Codecademy](https://mobile.twitter.com/JoshuaKGoldberg/status/1159090281314160640)) -- [TypeWiz](https://github.com/urish/typewiz) -- [js-to-ts-converter](https://github.com/gregjacobs/js-to-ts-converter) - -### Manual JS to TS Conversion - -the "Just Renaming" strategy - -- OSX/Linux: `find src -name "*.js" -exec sh -c 'mv"$0" "${0%.js}.tsx"' {} \;` - -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: - -```json -{ - "compilerOptions": { - "allowJs": true - } -} -``` - -Then you will want to enable it to check JS: - -```json -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true - } -} -``` - -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. - -TypeScript should throw up some egregious errors here which should be easy to fix. - -Once you are done, swallow the red pill by turning off implicit `any`'s: - -```js -{ - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "noImplicitAny": true // or "strict": true - } -} -``` - -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. - -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: - -```ts -type TODO_TYPEME = any; -export function myFunc(foo: TODO_TYPEME, bar: TODO_TYPEME): number { - // ... -} -``` - -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: - -```js -{ - "compilerOptions": { - "strict": true - } -} -``` - -**More resources** - -- [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) -- [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` -- [Migrating an EJECTED CRA app to TS](https://spin.atomicobject.com/2018/07/04/migrating-cra-typescript/) -- [Lyft's JS to TS migration tool](https://github.com/lyft/react-javascript-to-typescript-transform) (includes PropTypes migration) -- [Hootsuite][hootsuite] -- [Storybook's migration (PR)](https://github.com/storybooks/storybook/issues/5030) -- [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 -- incrementally adding strict null checks https://code.visualstudio.com/blogs/2019/05/23/strict-null - -Old content that is possibly out of date - -- [Incrementally Migrating JS to TS][clayallsop] (old) -- [Microsoft's TypeScript React Conversion Guide][mstsreactconversionguide] (old) - -## 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) -- [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 -- Quick-n-dirty [Flow to TS Codemod](https://gist.github.com/skovhus/c57367ce6ecbc3f70bb7c80f25727a11) -- [Ecobee's brief experience](https://mobile.twitter.com/alanhietala/status/1104450494754377728) -- [Migrating a 50K SLOC Flow + React Native app to TypeScript](https://blog.usejournal.com/migrating-a-flow-react-native-app-to-typescript-c74c7bceae7d) - -## Results - -- Number of production deploys doubled for [Hootsuite][hootsuite] -- Found accidental globals for [Tiny][tiny] -- Found incorrect function calls for [Tiny][tiny] -- Found rarely used, buggy code that was untested for [Tiny][tiny] - -## Academic Studies of Migration - -- [To Type or Not to Type: Quantifying Detectable Bugs in JavaScript](http://earlbarr.com/publications/typestudy.pdf) - -> 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%! - -## Misc migration stories by notable companies and open source - -- [Adopting TypeScript at Scale - AirBnB's conversion story and strategy](https://www.youtube.com/watch?v=P-J9Eg7hJwE) -- [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) -- [Slack](https://slack.engineering/typescript-at-slack-a81307fa288d) ([podcast](https://softwareengineeringdaily.com/2017/08/11/typescript-at-slack-with-felix-rieseberg/)) -- [Netflix adoption story](https://www.youtube.com/watch?v=p5Hwb1YbNMY&feature=share) -- [Priceline](https://medium.com/priceline-labs/trying-out-typescript-part-1-15a5267215b9) -- Dropbox - [Talk at React Loop](https://www.youtube.com/watch?v=veXkJq0Z2Qk) -- [Heap - How we failed, then succeeded, at migrating to TypeScript](https://heap.io/blog/analysis/migrating-to-typescript) - -Open Source - -- [Jest's migration (PR)](https://github.com/facebook/jest/pull/7554#issuecomment-454358729) -- [Expo's migration (issue)](https://github.com/expo/expo/issues/2164) -- [Google Workbox migration](https://github.com/GoogleChrome/workbox/pull/2058) -- [Chrome Dev Tools related issues](https://twitter.com/TimvdLippe/status/1220393069792694281) -- [Atlassian's migration (PR)](https://github.com/atlassian/react-beautiful-dnd/issues/982) -- [Yarn's migration (issue)](https://github.com/yarnpkg/yarn/issues/6953) -- [React Native CLI](https://github.com/react-native-community/cli/issues/683) -- [Next.js](https://nextjs.org/blog/next-9) -- [React Router](https://github.com/ReactTraining/react-router/issues/6955) - - [history](https://github.com/ReactTraining/history/pull/774) -- [Redux](https://github.com/reduxjs/redux/pull/3536) -- [Dojo 1 -> 2 migration](https://devchat.tv/js-jabber/jsj-277-dojo-2-dylan-schiemann-kitson-kelly/) - -## Links - -[hootsuite]: https://medium.com/hootsuite-engineering/thoughts-on-migrating-to-typescript-5e1a04288202 "Thoughts on migrating to TypeScript" -[clayallsop]: https://medium.com/@clayallsopp/incrementally-migrating-javascript-to-typescript-565020e49c88 "Incrementally Migrating JavaScript to TypeScript" -[pleo]: https://medium.com/pleo/migrating-a-babel-project-to-typescript-af6cd0b451f4 "Migrating a Babel project to TypeScript" -[mstsreactconversionguide]: https://github.com/Microsoft/TypeScript-React-Conversion-Guide "TypeScript React Conversion Guide" -[entria]: https://medium.com/entria/incremental-migration-to-typescript-on-a-flowtype-codebase-515f6490d92d "Incremental Migration to TypeScript on a Flowtype codebase" -[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" -[tiny]: https://go.tiny.cloud/blog/benefits-of-gradual-strong-typing-in-javascript/ "Benefits of gradual strong typing in JavaScript" diff --git a/README.md b/README.md index 8d4b31dc..c4841b3f 100644 --- a/README.md +++ b/README.md @@ -13,10 +13,7 @@

    Cheatsheets for experienced React developers getting started with TypeScript

    -[**Basic**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#basic-cheatsheet-table-of-contents) | -[**Advanced**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/ADVANCED.md) | -[**Migrating**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/MIGRATING.md) | -[**HOC**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/HOC.md) | +[**Web docs**](https://raulfdm.github.io/react-typescript-cheatsheet/docs/basic/setup) | [中文翻译](https://github.com/fi3ework/blog/tree/master/react-typescript-cheatsheet-cn) | [**Español**](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet-es) | [Contribute!](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/blob/master/CONTRIBUTING.md) | @@ -36,1869 +33,8 @@ [![All Contributors](https://img.shields.io/github/contributors/typescript-cheatsheets/react-typescript-cheatsheet?color=orange&style=flat-square)](/CONTRIBUTORS.md) -## All React + TypeScript Cheatsheets - -- **The Basic Cheatsheet** ([`/README.md`](/README.md#basic-cheatsheet-table-of-contents)) is focused on helping React devs just start using TS in React **apps** - - focus on opinionated best practices, copy+pastable examples - - explains some basic TS types usage and setup along the way - - answers the most Frequently Asked Questions - - does not cover generic type logic in detail. Instead we prefer to teach simple troubleshooting techniques for newbies. - - The goal is to get effective with TS without learning _too much_ TS. -- **The Advanced Cheatsheet** ([`/ADVANCED.md`](/ADVANCED.md)) 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**. - - It also has miscellaneous tips and tricks for pro users. - - Advice for contributing to DefinitelyTyped - - The goal is to take _full advantage_ of TypeScript. -- **The Migrating Cheatsheet** ([`/MIGRATING.md`](/MIGRATING.md)) helps collate advice for incrementally migrating large codebases from JS or Flow, **from people who have done it**. - - We do not try to convince people to switch, only to help people who have already decided - - ⚠️This is a new cheatsheet, all assistance is welcome -- **The HOC Cheatsheet** ([`/HOC.md`](/HOC.md)) specifically teaches people to write HOCs with examples. - - Familiarity with [Generics](https://www.typescriptlang.org/docs/handbook/generics.html) is necessary. - - ⚠️This is the newest cheatsheet, all assistance is welcome - ---- - -### Basic Cheatsheet Table of Contents - -
    - -Expand Table of Contents - -- [Section 1: Setup](#section-1-setup) - - [Prerequisites](#prerequisites) - - [React + TypeScript Starter Kits](#react--typescript-starter-kits) - - [Import React](#import-react) -- [Section 2: Getting Started](#section-2-getting-started) - - [Function Components](#function-components) - - [Hooks](#hooks) - - [Class Components](#class-components) - - [Typing defaultProps](#typing-defaultprops) - - [Types or Interfaces?](#types-or-interfaces) - - [Basic Prop Types Examples](#basic-prop-types-examples) - - [Useful React Prop Type Examples](#useful-react-prop-type-examples) - - [getDerivedStateFromProps](#getDerivedStateFromProps) - - [Forms and Events](#forms-and-events) - - [Context](#context) - - [forwardRef/createRef](#forwardrefcreateref) - - [Portals](#portals) - - [Error Boundaries](#error-boundaries) - - [Concurrent React/React Suspense](#concurrent-reactreact-suspense) -- [Basic Troubleshooting Handbook: Types](#basic-troubleshooting-handbook-types) - - [Union Types and Type Guarding](#union-types-and-type-guarding) - - [Optional Types](#optional-types) - - [Enum Types](#enum-types) - - [Type Assertion](#type-assertion) - - [Intersection Types](#intersection-types) - - [Using Inferred Types](#using-inferred-types) - - [Using Partial Types](#using-partial-types) - - [The Types I need Weren't Exported!](#the-types-i-need-werent-exported) -- [Troubleshooting Handbook: Operators](#troubleshooting-handbook-operators) -- [Troubleshooting Handbook: Utilties](#troubleshooting-handbook-utilties) -- [Troubleshooting Handbook: tsconfig.json](#troubleshooting-handbook-tsconfigjson) -- [Recommended React + TypeScript codebases to learn from](#recommended-react--typescript-codebases-to-learn-from) -- [Recommended React + TypeScript talks](#recommended-react--typescript-talks) -- [Editor Tooling and Integration](#editor-tooling-and-integration) -- [Linting](#linting) -- [Other React + TypeScript resources](#other-react--typescript-resources) -- [Time to Really Learn TypeScript](#time-to-really-learn-typescript) -- [Example App](#example-app) -- [My question isn't answered here!](#my-question-isnt-answered-here) -
    - -# Section 1: Setup - -## Prerequisites - -1. good understanding of [React](https://reactjs.org) -2. familiarity with [TypeScript Types](https://www.typescriptlang.org/docs/handbook/basic-types.html) ([2ality's guide](http://2ality.com/2018/04/type-notation-typescript.html) is helpful. If you’re an absolute beginner in TypeScript, check out [chibicode’s tutorial](https://ts.chibicode.com/todo/).) -3. having read [the TypeScript section in the official React docs](https://reactjs.org/docs/static-type-checking.html#typescript). -4. having read [the React section of the new TypeScript playground](http://www.typescriptlang.org/play/index.html?jsx=2&esModuleInterop=true&e=181#example/typescript-with-react) (optional: also step through the 40+ examples under [the playground's](http://www.typescriptlang.org/play/index.html) Examples section) - -This guide will always assume you are starting with the latest TypeScript version. Notes for older versions will be in expandable `
    ` tags. - -## React + TypeScript Starter Kits - -1. [Create React App v2.1+ with Typescript](https://facebook.github.io/create-react-app/docs/adding-typescript): `npx create-react-app my-app --template typescript` - -- We used to recommend `create-react-app-typescript` but it is now [deprecated](https://www.reddit.com/r/reactjs/comments/a5919a/createreactapptypescript_has_been_archived_rip/). [see migration instructions](https://vincenttunru.com/migrate-create-react-app-typescript-to-create-react-app/) - -2. [Basarat's guide](https://github.com/basarat/typescript-react/tree/master/01%20bootstrap) for **manual setup** of React + TypeScript + Webpack + Babel - -- In particular, make sure that you have `@types/react` and `@types/react-dom` installed ([Read more about the DefinitelyTyped project if you are unfamiliar](https://definitelytyped.org/)) -- There are also many React + TypeScript boilerplates, please see [our Resources list below](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet#recommended-react--typescript-codebases-to-learn-from). - -## Import React - -```tsx -import * as React from "react"; -import * as ReactDOM from "react-dom"; -``` - -In [TypeScript 2.7+](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html), you can run TypeScript with `--allowSyntheticDefaultImports` (or add `"allowSyntheticDefaultImports": true` to tsconfig) to import like in regular jsx: - -```tsx -import React from "react"; -import ReactDOM from "react-dom"; -``` - -
    - -Explanation - -Why `allowSyntheticDefaultImports` over `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) has said that it's better for webpack/parcel. For more discussion check out - -You should also check [the new TypeScript docs for official descriptions between each compiler flag](https://www.typescriptlang.org/v2/en/tsconfig#allowSyntheticDefaultImports)! - -
    - -# Section 2: Getting Started - -## 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 */ -const App = ({ message }: AppProps) =>
    {message}
    ; -``` - -
    - -What about `React.FC`/`React.FunctionComponent`? - -You can also write components with `React.FunctionComponent` (or the shorthand `React.FC` - they are the same): - -```tsx -const App: React.FunctionComponent<{ message: string }> = ({ message }) => ( -
    {message}
    -); -``` - -Some differences from the "normal function" version: - -- `React.FunctionComponent` is explicit about the return type, while the normal function version is implicit (or else needs additional annotation). - -- It provides typechecking and autocomplete for static properties like `displayName`, `propTypes`, and `defaultProps`. - - - Note that there are some known issues using `defaultProps` with `React.FunctionComponent`. See [this issue for details](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87). We maintain a separate `defaultProps` section you can also look up. - -- It provides an implicit definition of `children` (see below) - however there are some issues with the implicit `children` type (e.g. [DefinitelyTyped#33006](https://github.com/DefinitelyTyped/DefinitelyTyped/issues/33006)), and it might considered better style to be explicit about components that consume `children`, anyway. - -```tsx -const Title: React.FunctionComponent<{ title: string }> = ({ - children, - title, -}) =>
    {children}
    ; -``` - -- _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. - -In most cases it makes very little difference which syntax is used, but you may prefer the more explicit nature of `React.FunctionComponent`. - -
    - -
    -Minor Pitfalls - -These patterns are not supported: - -**Conditional rendering** - -```tsx -const MyConditionalComponent = ({ shouldRender = false }) => - shouldRender ?
    : false; // don't do this in JS either -const el = ; // throws an error -``` - -This is because due to limitations in the compiler, function components cannot return anything other than a JSX expression or `null`, otherwise it complains with a cryptic error message saying that the other type is not assignable to `Element`. - -```tsx -const MyArrayComponent = () => Array(5).fill(
    ); -const el2 = ; // throws an error -``` - -**Array.fill** - -Unfortunately just annotating the function type will not help so if you really need to return other exotic types that React supports, you'd need to perform a type assertion: - -```tsx -const MyArrayComponent = () => (Array(5).fill(
    ) as any) as JSX.Element; -``` - -[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). - -
    - -## 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: - -```tsx -const [val, toggle] = React.useState(false); // `val` is inferred to be a boolean, `toggle` only takes booleans -``` - -See also the [Using Inferred Types](#using-inferred-types) section if you need to use a complex type that you've relied on inference for. - -However, many hooks are initialized with null-ish default values, and you may wonder how to provide types. Explicitly declare the type, and use a union type: - -```tsx -const [user, setUser] = React.useState(null); - -// later... -setUser(newUser); -``` - -**useRef** - -When using `useRef`, you have two options when creating a ref container that does not have an initial value: - -```ts -const ref1 = useRef(null!); -const ref2 = useRef(null); -``` - -The first option will make `ref1.current` read-only, 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). - -The second option will make `ref2.current` mutable, and is intended for "instance variables" that you manage yourself. - -**useEffect** - -When using `useEffect`, take care not to return anything other than a function or `undefined`, otherwise both TypeScript and React will yell at you. This can be subtle when using arrow functions: - -```ts -function DelayedEffect(props: { timerMs: number }) { - const { timerMs } = props; - // bad! setTimeout implicitly returns a number because the arrow function body isn't wrapped in curly braces - useEffect( - () => - setTimeout(() => { - /* do stuff */ - }, timerMs), - [timerMs] - ); - return null; -} -``` - -**useRef** - -```tsx -function TextInputWithFocusButton() { - // initialise with null, but tell TypeScript we are looking for an HTMLInputElement - const inputEl = React.useRef(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! */} - - - - ); -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wFgAoCzAVwDsNgJa4AVJADxgElaxqYA6sBgALAGIQ01AM4AhfjCYAKAJRwA3hThwA9DrjBaw4CgA2waUjgB3YSLi1qp0wBo4AI35wYSZ6wCeYEgAymhQwGDw1lYoRHCmEBAA1oYA5nCY0HAozAASLACyADI8fDAAoqZIIEi0MFpwaEzS8IZllXAAvIjEMAB0MkjImAA8+cWl-JXVtTAAfEqOzioA3A1NtC1wTPIwirQAwuZoSV1wql1zGg3aenAt4RgOTqaNIkgn0g5ISAAmcDJvBA3h9TsBMAZeFNXjl-lIoEQ6nAOBZ+jddPpPPAmGgrPDEfAUS1pG5hAYvhAITBAlZxiUoRUqjU6m5RIDhOi7iIUF9RFYaqIIP9MlJpABCOCAUHJ0eDzm1oXAAGSKyHtUx9fGzNSacjaPWq6Ea6gI2Z9EUyVRrXV6gC+DRtVu0RBgxuYSnRIzm6O06h0ACpIdlfr9jExSQyOkxTP5GjkPFZBv9bKIDYSmbNpH04ABNFD+CV+nR2636kby+BETCddTlyo27w0zr4HycfC6L0lvUjLH7baHY5Jas7BRMI7AE42uYSUXed6pkY6HtMDulnQruCrCg2oA) - -example from [Stefan Baumgartner](https://fettblog.eu/typescript-react/hooks/#useref) - -**useReducer** - -You can use [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) for reducer actions. Don't forget to define the return type of reducer, otherwise Typescript will infer it. - -```tsx -type AppState = {}; -type Action = - | { type: "SET_ONE"; payload: string } - | { type: "SET_TWO"; payload: number }; - -export function reducer(state: AppState, action: Action): AppState { - switch (action.type) { - case "SET_ONE": - return { - ...state, - one: action.payload, // `payload` is string - }; - case "SET_TWO": - return { - ...state, - two: action.payload, // `payload` is number - }; - default: - return state; - } -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/C4TwDgpgBAgmYGVgENjQLxQN4F8CwAUKJLAMbACWA9gHZTqFRQA+2UxEAXFAEQICiAFQD6AeQBy-HgG4oYZCAA2VZABNuAZ2AAnCjQDmUfASass7cF14CRggOqiZchcrXcaAVwC2AIwjajaUJCCAAPMCptYCgAMw8acmo6bQhVD1J-AAotVCs4RBQ0ABooZETabhhymgBKSvgkXOxGKA0AdwpgUgALKEyyyloAOg4a5pMmKFJkDWg+ITFJHk4WyagU4A9tOixVtaghw5zivbXaKwGkofklFVUoAHoHqAADG9dVF6gKDVadPX0p0Ce2ms2sC3sjhWEzWGy2OyBTEOQ2OECKiPYbSo3Euw3ed0ezzeLjuXx+UE8vn8QJwQRhUFUEBiyA8imA0P26wgm22f1ydKYxhwQA) - -**Custom Hooks** - -If you are returning an array in your Custom Hook, you will want to avoid type inference as Typescript will infer a union type (when you actually want different types in each position of the array). Instead, use [TS 3.4 const assertions](https://devblogs.microsoft.com/typescript/announcing-typescript-3-4/#const-assertions): - -```tsx -export function useLoading() { - const [isLoading, setState] = React.useState(false); - const load = (aPromise: Promise) => { - setState(true); - return aPromise.finally(() => setState(false)); - }; - return [isLoading, load] as const; // infers [boolean, typeof load] instead of (boolean | typeof load)[] -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?target=5&jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCpAD0ljkwFcA7DYCZuRgZyQBkIKACbBmAcwAUASjgBvCnDhoO3eAG1g3AcNFiANHF4wAyjBQwkAXTgBeRMRgA6HklPmkEzCgA2vKQG4FJRV4b0EhWzgJFAAFHBBNJAAuODjcRIAeFGYATwA+GRs8uSDFIzcLCRgoRiQA0rgiGEYoTlj4xMdMUR9vHIlpW2Lys0qvXzr68kUAX0DpxqRm1rgNLXDdAzDhaxRuYOZVfzgAehO4UUwkKH21ACMICG9UZgMYHLAkCEw4baFrUSqVARb5RB5PF5wAA+cHen1BfykaksFBmQA) - -This way, when you destructure you actually get the right types based on destructure position. - -
    -Alternative: Asserting a tuple return type - -If you are [having trouble with const assertions](https://github.com/babel/babel/issues/9800), you can also assert or define the function return types: - -```tsx -export function useLoading() { - const [isLoading, setState] = React.useState(false); - const load = (aPromise: Promise) => { - setState(true); - return aPromise.finally(() => setState(false)); - }; - return [isLoading, load] as [ - boolean, - (aPromise: Promise) => Promise - ]; -} -``` - -A helper function that automatically types tuples can also be helpful if you write a lot of custom hooks: - -```ts -function tuplify(...elements: T) { - return elements; -} - -function useArray() { - const numberValue = useRef(3).current; - const functionValue = useRef(() => {}).current; - return [numberValue, functionValue]; // type is (number | (() => void))[] -} - -function useTuple() { - const numberValue = useRef(3).current; - const functionValue = useRef(() => {}).current; - return tuplify(numberValue, functionValue); // type is [number, () => void] -} -``` - -
    - -Note that the React team recommends that custom hooks that return more than two values should use proper objects instead of tuples, however. - -More Hooks + TypeScript reading: - -- https://medium.com/@jrwebdev/react-hooks-in-typescript-88fce7001d0d -- https://fettblog.eu/typescript-react/hooks/#useref - -If you are writing a React Hooks library, don't forget that you should also expose your types for users to use. - -Example React Hooks + TypeScript Libraries: - -- https://github.com/mweststrate/use-st8 -- https://github.com/palmerhq/the-platform -- https://github.com/sw-yx/hooks - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Class Components - -Within TypeScript, `React.Component` is a generic type (aka `React.Component`), so you want to provide it with (optional) prop and state type parameters: - -```tsx -type MyProps = { - // using `interface` is also ok - message: string; -}; -type MyState = { - count: number; // like this -}; -class App extends React.Component { - state: MyState = { - // optional second annotation for better type inference - count: 0, - }; - render() { - return ( -
    - {this.props.message} {this.state.count} -
    - ); - } -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgFlqAFHMAZzgF44BvCuHAD0QuAFd2wAHYBzOAANpMJFEzok8uME4oANuwhwIAawFwQSduxQykALjjsYUaTIDcFAL4fyNOo2oAZRgUZW4+MzQIMSkYBykxEAAjFTdhUV1gY3oYAAttLx80XRQrOABBMDA4JAAPZSkAE05kdBgAOgBhXEgpJFiAHiZWCA4AGgDg0KQAPgjyQSdphyYpsJ5+BcF0ozAYYAgpPUckKKa4FCkpCBD9w7hMaDgUmGUoOD96aUwVfrQkMyCKIxOJwAAMZm8ZiITRUAAoAJTzbZwIgwMRQKRwOGA7YDRrAABuM1xKN4eW07TAbHY7QsVhsSE8fAptKWynawNinlJcAGQgJxNxCJ8gh55E8QA) - -Don't forget that you can export/import/extend these types/interfaces for reuse. - -
    -Why annotate `state` twice? - -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. - -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. - -[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). - -
    - -
    - No need for readonly - -You often see sample code include `readonly` to mark props and state immutable: - -```tsx -type MyProps = { - readonly message: string; -}; -type MyState = { - readonly count: number; -}; -``` - -This is not necessary as `React.Component` already marks them as immutable. ([See PR and discussion!](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813)) - -
    - -**Class Methods**: Do it like normal, but just remember any arguments for your functions also need to be typed: - -```tsx -class App extends React.Component<{ message: string }, { count: number }> { - state = { count: 0 }; - render() { - return ( -
    this.increment(1)}> - {this.props.message} {this.state.count} -
    - ); - } - increment = (amt: number) => { - // like this - this.setState((state) => ({ - count: state.count + amt, - })); - }; -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN5wQSBigDmSAFxw6MKMB5q4AXwA0cRWggBXHjG09rIAEZIoJgHwWKcHTBTccAC8FnBWtvZwAAwmANw+cET8bgAUAJTe5L6+RDDWUDxwKQnZcLJ8wABucBA8YtTAaADWQfLpwV4wABbAdCIGaETKdikAjGnGHiWlFt29ImA4YH3KqhrGsz19ugFIIuF2xtO+sgD0FZVTWdlp8ddH1wNDMsFFKCCRji5uGUFe8tNTqc4A0mkg4HM6NNISI6EgYABlfzcFI7QJ-IoA66lA6RNF7XFwADUcHeMGmxjStwSxjuxiAA) - -**Class Properties**: If you need to declare class properties for later use, just declare it like `state`, but without assignment: - -```tsx -class App extends React.Component<{ - message: string; -}> { - pointer: number; // like this - componentDidMount() { - this.pointer = 3; - } - render() { - return ( -
    - {this.props.message} and {this.pointer} -
    - ); - } -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeAN4U4cEEgYoA5kgBccOjCjAeGgNwUAvgD44i8sshHuUXTwCuIAEZIoJuAHo-OGpgAGskOBgAC2A6JTg0SQhpHhgAEWA+AFkIVxSACgBKGzjlKJiRBxTvOABeOABmMzs4cziifm9C4ublIhhXKB44PJLlOFk+YAA3S1GxmzK6CpwwJdV1LXM4FH4F6KXKp1aesdk-SZnRgqblY-MgA) - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Typing defaultProps - -For Typescript 3.0+, type inference [should work](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html), although [some edge cases are still problematic](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/61). Just type your props like normal, except don't use `React.FC`. - -```tsx -// //////////////// -// function components -// //////////////// -type Props = { age: number } & typeof defaultProps; -const defaultProps = { - who: "Johny Five", -}; - -const Greet = (props: Props) => { - /*...*/ -}; -Greet.defaultProps = defaultProps; -``` - -For **Class components**, there are [a couple ways to do it](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/103#issuecomment-481061483)(including using the `Pick` utility type) but the recommendation is to "reverse" the props definition: - -```tsx -type GreetProps = typeof Greet.defaultProps & { - age: number; -}; - -class Greet extends React.Component { - static defaultProps = { - name: "world", - }; - /*...*/ -} - -// Type-checks! No type assertions needed! -let el = ; -``` - -
    - Why does React.FC break defaultProps? - -You can check the discussions here: - -- https://medium.com/@martin_hotell/10-typescript-pro-tips-patterns-with-or-without-react-5799488d6680 -- https://github.com/DefinitelyTyped/DefinitelyTyped/issues/30695 -- https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/87 - -This is just the current state and may be fixed in future. - -
    - -
    - Typescript 2.9 and earlier - -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: - -```ts -type Props = Required & { - /* additional props here */ -}; - -export class MyComponent extends React.Component { - static defaultProps = { - foo: "foo", - }; -} -``` - -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! - -```ts -interface IMyComponentProps { - firstProp?: string; - secondProp: IPerson[]; -} - -export class MyComponent extends React.Component { - public static defaultProps: Partial = { - firstProp: "default", - }; -} -``` - -The problem with this approach is it causes complex issues with the type inference working with `JSX.LibraryManagedAttributes`. Basically it causes the compiler to think that when creating a JSX expression with that component, that all of its props are optional. - -[See commentary by @ferdaber here](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57). - -
    - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Types or Interfaces? - -`interface`s are different from `type`s in TypeScript, but they can be used for very similar things as far as common React uses cases are concerned. Here's a helpful rule of thumb: - -- always use `interface` for public API's definition when authoring a library or 3rd party ambient type definitions. - -- consider using `type` for your React Component Props and State, because it is more constrained. - -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. - -
    - - Useful table for Types vs Interfaces - -It's a nuanced topic, don't get too hung up on it. Here's a handy graphic: - -![https://pbs.twimg.com/media/DwV-oOsXcAIct2q.jpg](https://pbs.twimg.com/media/DwV-oOsXcAIct2q.jpg) (source: [Karol Majewski](https://twitter.com/karoljmajewski/status/1082413696075382785)) - -
    - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Basic Prop Types Examples - -```tsx -type AppProps = { - message: string; - count: number; - disabled: boolean; - /** array of a type! */ - names: string[]; - /** string literals to specify exact string values, with a union type to join them together */ - status: "waiting" | "success"; - /** any object as long as you dont use its properties (not common) */ - obj: object; - obj2: {}; // almost the same as `object`, exactly the same as `Object` - /** an object with defined properties (preferred) */ - obj3: { - id: string; - title: string; - }; - /** array of objects! (common) */ - objArr: { - id: string; - title: string; - }[]; - /** any function as long as you don't invoke it (not recommended) */ - onSomething: Function; - /** function that doesn't take or return anything (VERY COMMON) */ - onClick: () => void; - /** function with named prop (VERY COMMON) */ - onChange: (id: number) => void; - /** alternative function type syntax that takes an event (VERY COMMON) */ - onClick(event: React.MouseEvent): void; - /** an optional prop (VERY COMMON!) */ - optional?: OptionalType; -}; -``` - -Notice we have used the TSDoc `/** comment */` style here on each prop. You can and are encouraged to leave descriptive comments on reusable components. For a fuller example and discussion, see our [Commenting Components](/ADVANCED.md#commenting-components) section in the Advanced Cheatsheet. - -## Useful React Prop Type Examples - -```tsx -export declare interface AppProps { - children1: JSX.Element; // bad, doesnt account for arrays - children2: JSX.Element | JSX.Element[]; // meh, doesn't accept strings - children3: React.ReactChildren; // despite the name, not at all an appropriate type; it is a utility - children4: React.ReactChild[]; // better - children: React.ReactNode; // best, accepts everything - functionChildren: (name: string) => React.ReactNode; // recommended function as a child render prop type - style?: React.CSSProperties; // to pass through style props - onChange?: React.FormEventHandler; // form events! the generic parameter is the type of event.target - props: Props & React.PropsWithoutRef; // to impersonate all the props of a button element without its ref -} -``` - -
    - JSX.Element vs React.ReactNode? - -Quote [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/57): A more technical explanation is that a valid React node is not the same thing as what is returned by `React.createElement`. Regardless of what a component ends up rendering, `React.createElement` always returns an object, which is the `JSX.Element` interface, but `React.ReactNode` is the set of all possible return values of a component. - -- `JSX.Element` -> Return value of `React.createElement` -- `React.ReactNode` -> Return value of a component -
    - -[More discussion: Where ReactNode does not overlap with JSX.Element](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/129) - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## getDerivedStateFromProps - -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 easily achieved using hooks which can also help set up memoization easily. - -Here are a few ways in which you can annotate `getDerivedStateFromProps` - -1. If you have explicitly typed your derived state and want to make sure that the return value from `getDerivedStateFromProps` conforms to it. - -```tsx -class Comp extends React.Component { - static getDerivedStateFromProps( - props: Props, - state: State - ): Partial | null { - // - } -} -``` - -2. When you want the function's return value to determine your state. - -```tsx -class Comp extends React.Component< - Props, - ReturnType -> { - static getDerivedStateFromProps(props: Props) {} -} -``` - -3. When you want derived state with other state fields and memoization - -```tsx -type CustomValue = any; -interface Props { - propA: CustomValue; -} -interface DefinedState { - otherStateField: string; -} -type State = DefinedState & ReturnType; -function transformPropsToState(props: Props) { - return { - savedPropA: props.propA, // save for memoization - derivedState: props.propA, - }; -} -class Comp extends React.PureComponent { - constructor(props: Props) { - super(props); - this.state = { - otherStateField: "123", - ...transformPropsToState(props), - }; - } - static getDerivedStateFromProps(props: Props, state: State) { - if (isEqual(props.propA, state.savedPropA)) return null; - return transformPropsToState(props); - } -} -``` - -[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) - -## Forms and Events - -If performance is not an issue, 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): - -```tsx -const el = ( - -)); -``` - -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). - -More info: https://medium.com/@martin_hotell/react-refs-with-typescript-a32d56c4d315 - -You may also wish to do [Conditional Rendering with `forwardRef`](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/167). - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Portals - -Using `ReactDOM.createPortal`: - -```tsx -const modalRoot = document.getElementById("modal-root") as HTMLElement; -// assuming in your html file has a div with id 'modal-root'; - -export class Modal extends React.Component { - el: HTMLElement = document.createElement("div"); - - componentDidMount() { - modalRoot.appendChild(this.el); - } - - componentWillUnmount() { - modalRoot.removeChild(this.el); - } - - render() { - return ReactDOM.createPortal(this.props.children, this.el); - } -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoUSWRYmAEQHkBZObXAo9GAWgBNcZchTQQAdgGd4ICHxQAbBBAjwAvHAFoAriCRiYAOgDmSGAFF5SXfoBCATwCSfABQAiGXPk8cK1wEo4FAk4AAkAFWYAGQsrPRgAbgoAeiTAiQkdYDEjOCy4OwgtKDgACxgQeTZgS1KgwI1gADc4AHdgGBLcvgIPBW9lGHxE4XIkAA9qeDR5IODmWQU4cZg9PmDkbgMAYVxIMTi4AG8KOCX5AC5QiOjLazUNCG07gzQuFZi7tz4m-2GTuFE4HEcXowD48y0+mcAWO5FOp16igGBhQYDAqy2JWqLg6wAkBiQ8j8w1OAF8KP9AXs4gB1aryACqYhkkJg0KO-wRCyRKgMRBkjSQmOxzlx+MJxP+5JGpyIYj4SCg7Nh8LgRBgRTEtG4TGYLzeSAACtAYApRVj8WAcGB8WgsfI+HKADRwMUEokkuDS0lAA) - -
    - -Context of Example - -This example is based on the [Event Bubbling Through Portal](https://reactjs.org/docs/portals.html#event-bubbling-through-portals) example of React docs. - -
    - -## Error Boundaries - -_Not written yet._ - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -## Concurrent React/React Suspense - -_Not written yet._ watch for more on React Suspense and Time Slicing. - -[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new). - -# Basic Troubleshooting Handbook: Types - -> ⚠️ Have you read [the TypeScript FAQ](https://github.com/microsoft/TypeScript/wiki/FAQ)?) Your answer might be there! - -Facing weird type errors? You aren't alone. This is the hardest part of using TypeScript with React. Be patient - you are learning a new language after all. However, the more you get good at this, the less time you'll be working _against_ the compiler and the more the compiler will be working _for_ you! - -Try to avoid typing with `any` as much as possible to experience the full benefits of typescript. Instead, let's try to be familiar with some of the common strategies to solve these issues. - -## Union Types and Type Guarding - -Union types are handy for solving some of these typing problems: - -```tsx -class App extends React.Component< - {}, - { - count: number | null; // like this - } -> { - state = { - count: null, - }; - render() { - return
    this.increment(1)}>{this.state.count}
    ; - } - increment = (amt: number) => { - this.setState((state) => ({ - count: (state.count || 0) + amt, - })); - }; -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BBMMOJADxiQDsATRsnQwAdAGFckHrxgAeCnDgBvAL4AaBcs2K0EAK48YALjg89IAEZIocAD6m91agG44AejdxqwANZI4MAAWwHSaKhQAfFrkinQwKNxwALzRijr6hiZmTmHOmkT81gAUAJSpaUQwelA8cLJ8wABucBA8Yt5oPklKpclRQSEiwDxoRCAyRQCMJSoRSgN0InEJSCK6BjAqsm4NjRF5MXDhh8OjSOOGyXBFKCDGDpbWZUlRStoBwYt0SDAAyvHcIrLRIva5vQ5pODrTLXYGraHwWz2AAMZQA1HBbjB3ioSiUDooVAcVEA) - -**Type Guarding**: Sometimes Union Types solve a problem in one area but create another downstream. If `A` and `B` are both object types, `A | B` isn't "either A or B", it is "A or B or both at once", which causes some confusion if you expected it to be the former. Learn how to write checks, guards, and assertions (also see the Conditional Rendering section below). For example: - -```ts -interface Admin { - role: string; -} -interface User { - email: string; -} - -// Method 1: use `in` keyword -function redirect(user: Admin | User) { - if ("role" in user) { - // use the `in` operator for typeguards since TS 2.7+ - routeToAdminPage(user.role); - } else { - routeToHomePage(user.email); - } -} - -// Method 2: custom type guard, does the same thing in older TS versions or where `in` isnt enough -function isAdmin(user: Admin | User): user is Admin { - return (user as any).role !== undefined; -} -``` - -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgEEATEGuAbwrjhwAbJAC44AZxhQaAcwDcFAL5Va9RmmYBVcfR584SECmCCxk6dXlKKFTAFdqGYBGoCIdugBUI7TtQAKKDJIABTiwDLUwJjA9ACUeuT80XBhEVExugC8OQR2OlAIEML4CbxJ-AJIMHZQrvi+NGQVinDWlOT2jjDOrjgeSN4AErhIgcFpkdGxUGX6KZMZM3A5WQSGxoKliZVVNXUEIyBIYEFIzfzK5FcUAPS3cACy1QAWEGxwAIxi+cwABjQ-nAANZIACeAHdoGxbA4nC4qmxgEQMCFflAxI1XAAfODaeI7ODREIAIiESBJRNc6LKcHucF+cBgL3+gLgEDA9BQMGgcEwvJgYM5MjsKCgbHEEhoGjgngAynAAEwAOgA7ABqfT8fpeHwcGjjULo5XkuIKFoGQQ6Qna9y6o5jM5ogrKjYmM36K43cj057M95KsRofI8vCCzlwEVitgAGjgbAgSElzOY4hQxyZL1kVPZgjYunlcAAbvRwi5JbyISyiHAAdQgcBxLQDNR3DIXrDur0ieIsc76Jj9Ti8QU4j8Cj3WEPCUR9q5+1A4ChJShqGC4ibiswAIS5Bz5mLUJAw65AA) - -Method 2 is also known as [User-Defined Type Guards](https://www.typescriptlang.org/docs/handbook/advanced-types.html#user-defined-type-guards) and can be really handy for readable code. This is how TS itself refines types with `typeof` and `instanceof`. - -If you need `if...else` chains or the `switch` statement instead, it should "just work", but look up [Discriminated Unions](https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions) if you need help. (See also: [Basarat's writeup](https://basarat.gitbook.io/typescript/type-system/discriminated-unions)). This is handy in typing reducers for `useReducer` or Redux. - -## Optional Types - -If a component has an optional prop, add a question mark and assign during destructure (or use defaultProps). - -```tsx -class MyComponent extends React.Component<{ - message?: string; // like this -}> { - render() { - const { message = "default" } = this.props; - return
    {message}
    ; - } -} -``` - -You can also use a `!` character to assert that something is not undefined, but this is not encouraged. - -_Something to add? [File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new) with your suggestions!_ - -## Enum Types - -Enums in TypeScript default to numbers. You will usually want to use them as strings instead: - -```tsx -export enum ButtonSizes { - default = "default", - small = "small", - large = "large", -} -``` - -Usage: - -```tsx -export const PrimaryButton = ( - props: Props & React.HTMLProps -) =>
    + {/* 😭 Error, `disabled` doesnt exist on anchor element */} + + + ); +} +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoAekrgCEBXGGCAOzjBzAGcKYBPMEjqNmLAAqcucALyJiMAHQMmrABIAVALIAZAIJMowAEaMkXADwady0QFEANkhBIWMAHxwAZHADeFOHAAFkSYAPwAXHD0LAAmSJjALEgxANwUAL5p5BTUcLosaIHQ7JK8AkL5hdASENwycuiKlUVQVnoGxqYWbc3QDk4u7l6+-kEhEXBcMIYsAOZZmRQ5NACSLGCMlBCMG-C1MMCsPOT8gnAA8gBuSFD2ECgx9X7kAQAUHLVckTasNdwAlJEAFIAZQAGgp+s5XFk3h9uJFelA-lxAXBQRCoYMFlllnAAOL0FBQR7MOCFJBoADWcGAmDG8TgSAAHsAplJEiVPhQ0Ed4IEUFxVCF6u9JN8RL9JHAAD55AotFFo+EcqRIlEyNyjABEwXi2tpbBVuKoNAAwrhIElXDy+cIVCxIlcbncHqKVRKHRq5erJP9NSMXnBcigFcUiLEbqM6XBXgKhSExZ9-v6iDB6FA2OYUL4FHmVelg25YcGaCYHXAI3EoKM0xms+XRLn85JC5RixkTbkAKpcFCzJAUTDRDCHNi6MBgV7+54BOuZ2OjALmLVBgIBHyUABUcEAvBuAOD28vZ7HBZhAII8t5R0kv1+YfmwYMSBzBpNqAPpGeyhqkGvWYN9AiYBFqAAd3AhQzwgWZHAUXkQG1Vd12QuB1DMGBb2XSgHyQlDNx3XdAFo9uBbCgHAoAAGjgAADGI2RQL9kmouAYggMxXCZVkpjgVg4FDKooCZRxoXgK8bzXO8HxY+jGMef832ZRDMPXNCpmU8xsMlFhcKw3D-gWIA) diff --git a/docs/advanced/guides/extract-props.md b/docs/advanced/guides/extract-props.md new file mode 100644 index 00000000..722f65de --- /dev/null +++ b/docs/advanced/guides/extract-props.md @@ -0,0 +1,47 @@ +--- +id: extract_props +title: Props: Extracting Prop Types of a Component +--- + +_(Contributed by [@ferdaber](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/63))_ + +There are a lot of places where you want to reuse some slices of props because of prop drilling, +so you can either export the props type as part of the module or extract them (either way works). + +The advantage of extracting the prop types is that you won't need to export everything, and a refactor of the source of truth component will propagate to all consuming components. + +```ts +import { ComponentProps, JSXElementConstructor } from "react"; + +// goes one step further and resolves with propTypes and defaultProps properties +type ApparentComponentProps< + C extends keyof JSX.IntrinsicElements | JSXElementConstructor +> = C extends JSXElementConstructor + ? JSX.LibraryManagedAttributes + : ComponentProps; +``` + +You can also use them to strongly type custom event handlers if they're not written at the call sites themselves +(i.e. inlined with the JSX attribute): + +```tsx +// my-inner-component.tsx +export function MyInnerComponent(props: { + onSomeEvent( + event: ComplexEventObj, + moreArgs: ComplexArgs + ): SomeWeirdReturnType; +}) { + /* ... */ +} + +// my-consuming-component.tsx +export function MyConsumingComponent() { + // event and moreArgs are contextually typed along with the return value + const theHandler: Props["onSomeEvent"] = ( + event, + moreArgs + ) => {}; + return ; +} +``` diff --git a/docs/advanced/guides/generic-components.md b/docs/advanced/guides/generic-components.md new file mode 100644 index 00000000..c5393db3 --- /dev/null +++ b/docs/advanced/guides/generic-components.md @@ -0,0 +1,203 @@ +--- +id: generic_components +title: Generic Components +--- + +Just as you can make generic functions and classes in TypeScript, you can also make generic components to take advantage of the type system for reusable type safety. Both Props and State can take advantage of the same generic types, although it probably makes more sense for Props than for State. You can then use the generic type to annotate types of any variables defined inside your function / class scope. + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} +function List(props: Props) { + const { items, renderItem } = props; + const [state, setState] = React.useState([]); // You can use type T in List function scope. + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(state, null, 2)} +
    + ); +} +``` + +You can then use the generic components and get nice type safety through type inference: + +```tsx +ReactDOM.render( + ( +
  • + {/* Error: Property 'toPrecision' does not exist on type 'string'. */} + {item.toPrecision(3)} +
  • + )} + />, + document.body +); +``` + +As of [TS 2.9](#typescript-29), you can also supply the type parameter in your JSX to opt out of type inference: + +```tsx +ReactDOM.render( + + items={["a", "b"]} // Error: Type 'string' is not assignable to type 'number'. + renderItem={(item) =>
  • {item.toPrecision(3)}
  • } + />, + document.body +); +``` + +You can also use Generics using fat arrow function style: + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} + +// Note the before the function definition. +// You can't use just `` as it will confuse the TSX parser whether it's a JSX tag or a Generic Declaration. +// You can also use https://github.com/microsoft/TypeScript/issues/15713#issuecomment-499474386 +const List = (props: Props) => { + const { items, renderItem } = props; + const [state, setState] = React.useState([]); // You can use type T in List function scope. + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(state, null, 2)} +
    + ); +}; +``` + +The same for using classes: (Credit: [Karol Majewski](https://twitter.com/WrocTypeScript/status/1163234064343736326)'s [gist](https://gist.github.com/karol-majewski/befaf05af73c7cb3248b4e084ae5df71)) + +```tsx +interface Props { + items: T[]; + renderItem: (item: T) => React.ReactNode; +} + +interface State { + items: T[]; +} + +class List extends React.PureComponent, State> { + // You can use type T inside List class. + state: Readonly> = { + items: [], + }; + render() { + const { items, renderItem } = this.props; + // You can use type T inside List class. + const clone: T[] = items.slice(0); + return ( +
    + {items.map(renderItem)} + + {JSON.stringify(this.state, null, 2)} +
    + ); + } +} +``` + +Though you can't use Generic Type Parameters for Static Members: + +```tsx +class List extends React.PureComponent, State> { + // Static members cannot reference class type parameters.ts(2302) + static getDerivedStateFromProps(props: Props, state: State) { + return { items: props.items }; + } +} +``` + +To fix this you need to convert your static function to a type inferred function: + +```tsx +class List extends React.PureComponent, State> { + static getDerivedStateFromProps(props: Props, state: State) { + return { items: props.items }; + } +} +``` + +### Generic components with children + +`children` is usually not defined as a part of the props type. Unless `children` are explicitly defined as a part of the `props` type, an attempt to use `props.children` in JSX or in the function body will fail: + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; +} + +/* Property 'children' does not exist on type 'WrapperProps'. */ +const Wrapper = (props: WrapperProps) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; + +/* +Type '{ children: string; item: string; renderItem: (item: string) => string; }' is not assignable to type 'IntrinsicAttributes & WrapperProps'. + Property 'children' does not exist on type 'IntrinsicAttributes & WrapperProps'. +*/ + +const wrapper = ( + item}> + {test} + +); +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoC4AOxiSk3STgHUoUwx6AFHMAZwA8AFQB8cAN4U4cYHRAAuOMIDc0uEWoATegEl5SgBRyki5QEo4AXnHJ0MAHR2MAOQg615GWgAWwADZamkrOjqFuHhQAvhQUAPQAVHC8EFywAJ4EvgFBSNT4cFoQSPxw1BDwSAAewPzwENRwMOlcBGwcaSkCIqL4DnAJcRRoDXWs7Jz01nAicNV02qUSUaKGYHz8Su2TUF1CYpY2kupEMACuUI2G6jKCWsAAbqI3MpLrqfwOmjpQ+qZrGwcJhA5hiXleMgk7wEDmygU0YIhgji9ye6nMniinniCQowhazHwEjgcNy1CUdSgNAA5ipZAY4JSaXTvnoGcYGUzqNTDuIubS4FECrUyhU4Ch+PxgNTqCgAEb+ZgwCBNAkEXS0KnUKVoACCMBgVLlZzopQAZOMOjwNoJ+b0HOouvRmlk-PC8gUiiVRZUamMGqrWvgNYaaDr9aHjaa4Bbtp0bXa+hRBrFyCNtfBTfArHBDLyZqjRAAJJD+fwqrPIwvDUbwADuEzS02u4MEcamwKsACIs12NHkfn8QFYJMDrOJgSsXhIs4iZnF21BnuQMUA) + +To work around that, either add `children` to the `WrapperProps` definition (possibly narrowing down its type, as needed): + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; + children: string; // The component will only accept a single string child +} + +const Wrapper = (props: WrapperProps) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; +``` + +or wrap the type of the props in `React.PropsWithChildren` (this is what `React.FC<>` does): + +```tsx +interface WrapperProps { + item: T; + renderItem: (item: T) => React.ReactNode; +} + +const Wrapper = ( + props: React.PropsWithChildren> +) => { + return ( +
    + {props.renderItem(props.item)} + {props.children} +
    + ); +}; +``` diff --git a/docs/advanced/guides/handling-exceptions.md b/docs/advanced/guides/handling-exceptions.md new file mode 100644 index 00000000..e84a9445 --- /dev/null +++ b/docs/advanced/guides/handling-exceptions.md @@ -0,0 +1,109 @@ +--- +id: handling_exception +title: Handling Exceptions +--- + +You can provide good information when bad things happen. + +```ts +class InvalidDateFormatError extends RangeError {} +class DateIsInFutureError extends RangeError {} + +/** + * // optional docblock + * @throws {InvalidDateFormatError} The user entered date incorrectly + * @throws {DateIsInFutureError} The user entered date in future + * + */ +function parse(date: string) { + if (!isValid(date)) + throw new InvalidDateFormatError("not a valid date format"); + if (isInFuture(date)) throw new DateIsInFutureError("date is in the future"); + // ... +} + +try { + // call parse(date) somewhere +} catch (e) { + if (e instanceof InvalidDateFormatError) { + console.error("invalid date format", e); + } else if (e instanceof DateIsInFutureError) { + console.warn("date is in future", e); + } else { + throw e; + } +} +``` + +[View in TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCtAGxQGc64BJAOwDcVrgATAERRhIAYtBACAolBxQ4SAB6CW3RghQsA5kknS4AbwC+VWgzj9BTOqyEBXGNaLboshUiUq1mxzIMUKmaywYwBAscMB0AGqcPAAU3AJIAFxwdDBQwBoAlHoUcHBEdlCh8YJwAPxwadZIcMmYnHRIANwUhpTk-oEwwaHhVrb2SHEJyanpWTnkeWghqXAlSAByEADucAC8cCxIa2ZDmS1TcDMsc2j2RCwwextbO6YJw4KZuXCvBfah51Ku1wkAdJoYAAVUD7OAAPnmCWWK0BSBBYJiB1avnIAHoAFSY3KYuDo9FwCBgbohTjzCBoABG1EpAGtcXAAAIwAAWOBWjF0rA4XD4CREUDEMC8+jgwNZNWsjRkvyQRG40NKGRmPww1AAnoyWezVly9hZ+oUtFJoGKJVKZbIrvKkIqFmFQv5jbjcei-AEgiE4GAUFBGk8kik0hl1NldK9gJg4DEAIThKJ8wOZF5HPJsjl3NY86L8wSC4VeGIAIhYEHgKDgvJ4SpqmFEAmLKKOUZjfRYNmNyeyGdWWYe5ksHYGDlNUBLDvCjsqkrgzsGTcOeQJcH+a9R7TSGsmy8JaE41B9foDC2ydFwO0lRFaxwEaFZMaQ4cj0ZiNQyqTUaCQEGjOb5ewFhIY7PmmxyzBA1BIP88rSCWGTVvaCRzg2MDFgANLIzZ5GKSDUI0YSvu+pwwF+P7RgaQ6doMXigXk0wQVB-wrH6LATshU4ZHOI5IBhWFLnAuH4TUEZgb2azNK8bT6EAA) + +Simply throwing an exception is fine, however it would be nice to make TypeScript remind the consumer of your code to handle your exception. We can do that just by returning instead of throwing: + +```ts +function parse( + date: string +): Date | InvalidDateFormatError | DateIsInFutureError { + if (!isValid(date)) + return new InvalidDateFormatError("not a valid date format"); + if (isInFuture(date)) return new DateIsInFutureError("date is in the future"); + // ... +} + +// now consumer *has* to handle the errors +let result = parse("mydate"); +if (result instanceof InvalidDateFormatError) { + console.error("invalid date format", result.message); +} else if (result instanceof DateIsInFutureError) { + console.warn("date is in future", result.message); +} else { + /// use result safely +} + +// alternately you can just handle all errors +if (result instanceof Error) { + console.error("error", result); +} else { + /// use result safely +} +``` + +You can also describe exceptions with special-purpose data types (don't say monads...) like the `Try`, `Option` (or `Maybe`), and `Either` data types: + +```ts +interface Option { + flatMap(f: (value: T) => None): None; + flatMap(f: (value: T) => Option): FormikOption; + getOrElse(value: T): T; +} +class Some implements Option { + constructor(private value: T) {} + flatMap(f: (value: T) => None): None; + flatMap(f: (value: T) => Some): Some; + flatMap(f: (value: T) => Option): Option { + return f(this.value); + } + getOrElse(): T { + return this.value; + } +} +class None implements Option { + flatMap(): None { + return this; + } + getOrElse(value: U): U { + return value; + } +} + +// now you can use it like: +let result = Option(6) // Some + .flatMap((n) => Option(n * 3)) // Some + .flatMap((n = new None())) // None + .getOrElse(7); + +// or: +let result = ask() // Option + .flatMap(parse) // Option + .flatMap((d) => new Some(d.toISOString())) // Option + .getOrElse("error parsing string"); +``` diff --git a/docs/advanced/guides/hocs.md b/docs/advanced/guides/hocs.md new file mode 100644 index 00000000..ea005485 --- /dev/null +++ b/docs/advanced/guides/hocs.md @@ -0,0 +1,6 @@ +--- +id: hocs +title: Higher Order Components (HOCs) +--- + +**There is now a dedicated [HOC cheatsheet](./HOC.md) you can refer to get some practice on HOCs.** diff --git a/docs/advanced/guides/omit-attr.md b/docs/advanced/guides/omit-attr.md new file mode 100644 index 00000000..ebeb9e84 --- /dev/null +++ b/docs/advanced/guides/omit-attr.md @@ -0,0 +1,47 @@ +--- +id: omit_attr +title: Omit attribute from a type +--- + +Note: [Omit was added as a first class utility in TS 3.5](https://www.typescriptlang.org/docs/handbook/utility-types.html#omittk)! 🎉 + +Sometimes when intersecting types, we want to define our own version of an attribute. For example, I want my component to have a `label`, but the type I am intersecting with also has a `label` attribute. Here's how to extract that out: + +```tsx +export interface Props { + label: React.ReactNode; // this will conflict with the InputElement's label +} + +// this comes inbuilt with TS 3.5 +type Omit = Pick>; + +// usage +export const Checkbox = ( + props: Props & Omit, "label"> +) => { + const { label } = props; + return ( +
    + + {label} +
    + ); +}; +``` + +When your component defines multiple props, chances of those conflicts increase. However you can explicitly state that all your fields should be removed from the underlying component using the `keyof` operator: + +```tsx +export interface Props { + label: React.ReactNode; // conflicts with the InputElement's label + onChange: (text: string) => void; // conflicts with InputElement's onChange +} + +export const Textbox = ( + props: Props & Omit, keyof Props> +) => { + // implement Textbox component ... +}; +``` diff --git a/docs/advanced/guides/polymorphic-components.md b/docs/advanced/guides/polymorphic-components.md new file mode 100644 index 00000000..5a5266d9 --- /dev/null +++ b/docs/advanced/guides/polymorphic-components.md @@ -0,0 +1,23 @@ +--- +id: polymorphic_components +title: Polymorphic Components +--- + +> passing a component to be rendered, e.g. with `as` props + +`ElementType` is pretty useful to cover most types that can be passed to createElement e.g. + +```tsx +function PassThrough(props: { as: React.ElementType }) { + const { as: Component } = props; + + return ; +} +``` + +For more info you can refer to these resources: + +- https://blog.andrewbran.ch/polymorphic-react-components/ +- https://github.com/kripod/react-polymorphic-box + +[Thanks @eps1lon](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/pull/69) for this diff --git a/docs/advanced/guides/props-must-pass-both.md b/docs/advanced/guides/props-must-pass-both.md new file mode 100644 index 00000000..42ec17dd --- /dev/null +++ b/docs/advanced/guides/props-must-pass-both.md @@ -0,0 +1,18 @@ +--- +id: props_one_other_not_both +title: Props: Must Pass Both +--- + +```tsx +type OneOrAnother = + | (T1 & { [K in keyof T2]?: undefined }) + | (T2 & { [K in keyof T1]?: undefined }); + +type Props = OneOrAnother<{ a: string; b: string }, {}>; + +const a: Props = { a: "a" }; // error +const b: Props = { b: "b" }; // error +const ab: Props = { a: "a", b: "b" }; // ok +``` + +Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426) diff --git a/docs/advanced/guides/props-one-other-not-both.md b/docs/advanced/guides/props-one-other-not-both.md new file mode 100644 index 00000000..3895b95d --- /dev/null +++ b/docs/advanced/guides/props-one-other-not-both.md @@ -0,0 +1,32 @@ +--- +id: props_one_other_not_both +title: Props: One or the Other but not Both +--- + +Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both: + +```tsx +type Props1 = { foo: string }; +type Props2 = { bar: string }; + +function MyComponent(props: Props1 | Props2) { + if ("foo" in props) { + // props.bar // error + return
    {props.foo}
    ; + } else { + // props.foo // error + return
    {props.bar}
    ; + } +} +const UsageComponent: React.FC = () => ( +
    + + + {/* // invalid */} +
    +); +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) + +Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable) diff --git a/docs/advanced/guides/props-optionally-pass-one.md b/docs/advanced/guides/props-optionally-pass-one.md new file mode 100644 index 00000000..7d43fbe3 --- /dev/null +++ b/docs/advanced/guides/props-optionally-pass-one.md @@ -0,0 +1,51 @@ +--- +id: props_optionally_pass_one +title: Props: Can Optionally Pass One Only If the Other Is Passed +--- + +Say you want a Text component that gets truncated if `truncate` prop is passed but expands to show the full text when `expanded` prop is passed (e.g. when the user clicks the text). + +You want to allow `expanded` to be passed only if `truncate` is also passed, because there is no use for `expanded` if the text is not truncated. + +You can do this by function overloads: + +```tsx +type CommonProps = { + children: React.ReactNode; + miscProps?: any; +}; + +type NoTruncateProps = CommonProps & { truncate?: false }; + +type TruncateProps = CommonProps & { truncate: true; expanded?: boolean }; + +// Function overloads to accept both prop types NoTruncateProps & TruncateProps +function Text(props: NoTruncateProps): JSX.Element; +function Text(props: TruncateProps): JSX.Element; +function Text(props: CommonProps & { truncate?: boolean; expanded?: boolean }) { + const { children, truncate, expanded, ...otherProps } = props; + const classNames = truncate ? ".truncate" : ""; + return ( +
    + {children} +
    + ); +} +``` + +Using the Text component: + +```tsx +const App: React.FC = () => ( + <> + {/* these all typecheck */} + not truncated + truncated + + truncate-able but expanded + + {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type '{ truncate: true; expanded?: boolean | undefined; }'. */} + truncate-able but expanded + +); +``` diff --git a/docs/advanced/guides/render-props.md b/docs/advanced/guides/render-props.md new file mode 100644 index 00000000..f9e43c10 --- /dev/null +++ b/docs/advanced/guides/render-props.md @@ -0,0 +1,31 @@ +--- +id: render_props +title: Render Props +--- + +Sometimes you will want to write a function that can take a React element or a string or something else as a prop. The best Type to use for such a situation is `React.ReactNode` which fits anywhere a normal, well, React Node would fit: + +```tsx +export interface Props { + label?: React.ReactNode; + children: React.ReactNode; +} +export const Card = (props: Props) => { + return ( +
    + {props.label &&
    {props.label}
    } + {props.children} +
    + ); +}; +``` + +If you are using a function-as-a-child render prop: + +```tsx +export interface Props { + children: (foo: string) => React.ReactNode; +} +``` + +[Something to add? File an issue](https://github.com/typescript-cheatsheets/react-typescript-cheatsheet/issues/new/choose). diff --git a/docs/advanced/guides/third-party-libs.md b/docs/advanced/guides/third-party-libs.md new file mode 100644 index 00000000..c9f84c58 --- /dev/null +++ b/docs/advanced/guides/third-party-libs.md @@ -0,0 +1,6 @@ +--- +id: third_party_libs +title: Props: Third Party Libraries +--- + +Sometimes DefinitelyTyped can get it wrong, or isn't quite addressing your use case. You can declare your own file with the same interface name. Typescript will merge interfaces with the same name. diff --git a/docs/advanced/guides/type-component-diff-props.md b/docs/advanced/guides/type-component-diff-props.md new file mode 100644 index 00000000..a9201600 --- /dev/null +++ b/docs/advanced/guides/type-component-diff-props.md @@ -0,0 +1,171 @@ +--- +id: type_component_diff_props +title: Typing a Component that Accepts Different Props +--- + +Components, and JSX in general, are analogous to functions. When a component can render differently based on their props, it's similar to how a function can be overloaded to have multiple call signatures. In the same way, you can overload a function component's call signature to list all of its different "versions". + +A very common use case for this is to render something as either a button or an anchor, based on if it receives a `href` attribute. + +```tsx +type ButtonProps = JSX.IntrinsicElements["button"]; +type AnchorProps = JSX.IntrinsicElements["a"]; + +// optionally use a custom type guard +function isPropsForAnchorElement( + props: ButtonProps | AnchorProps +): props is AnchorProps { + return "href" in props; +} + +function Clickable(props: ButtonProps | AnchorProps) { + if (isPropsForAnchorElement(props)) { + return
    ; + } else { + return
    + + ); + } +} + +class Index extends React.Component { + render() { + const { config: siteConfig, language = "" } = this.props; + const { baseUrl } = siteConfig; + + const Block = (props) => ( + + + + ); + + const FeatureCallout = () => ( +
    +

    Feature Callout

    + These are features of this project +
    + ); + + const TryOut = () => ( + + {[ + { + content: + "To make your landing page more attractive, use illustrations! Check out " + + "[**unDraw**](https://undraw.co/) which provides you with customizable illustrations which are free to use. " + + "The illustrations you see on this page are from unDraw.", + image: `${baseUrl}img/undraw_code_review.svg`, + imageAlign: "left", + title: "Wonderful SVG Illustrations", + }, + ]} + + ); + + const Description = () => ( + + {[ + { + content: + "This is another description of how this project is useful", + image: `${baseUrl}img/undraw_note_list.svg`, + imageAlign: "right", + title: "Description", + }, + ]} + + ); + + const LearnHow = () => ( + + {[ + { + content: + "Each new Docusaurus project has **randomly-generated** theme colors.", + image: `${baseUrl}img/undraw_youtube_tutorial.svg`, + imageAlign: "right", + title: "Randomly Generated Theme Colors", + }, + ]} + + ); + + const Features = () => ( + + {[ + { + content: "This is the content of my feature", + image: `${baseUrl}img/undraw_react.svg`, + imageAlign: "top", + title: "Feature One", + }, + { + content: "The content of my second feature", + image: `${baseUrl}img/undraw_operating_system.svg`, + imageAlign: "top", + title: "Feature Two", + }, + ]} + + ); + + const Showcase = () => { + if ((siteConfig.users || []).length === 0) { + return null; + } + + const showcase = siteConfig.users + .filter((user) => user.pinned) + .map((user) => ( + + {user.caption} + + )); + + const pageUrl = (page) => + baseUrl + (language ? `${language}/` : "") + page; + + return ( +
    +

    Who is Using This?

    +

    This project is used by all these people

    +
    {showcase}
    + +
    + ); + }; + + return ; + } +} + +module.exports = Index; diff --git a/website/pages/en/users.js b/website/pages/en/users.js new file mode 100644 index 00000000..9147c97d --- /dev/null +++ b/website/pages/en/users.js @@ -0,0 +1,48 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +const React = require("react"); + +const CompLibrary = require("../../core/CompLibrary.js"); + +const Container = CompLibrary.Container; + +class Users extends React.Component { + render() { + const { config: siteConfig } = this.props; + if ((siteConfig.users || []).length === 0) { + return null; + } + + const editUrl = `${siteConfig.repoUrl}/edit/master/website/siteConfig.js`; + const showcase = siteConfig.users.map((user) => ( + + {user.caption} + + )); + + return ( +
    + +
    +
    +

    Who is Using This?

    +

    This project is used by many folks

    +
    +
    {showcase}
    +

    Are you using this project?

    + + Add your company + +
    +
    +
    + ); + } +} + +module.exports = Users; diff --git a/website/sidebars.json b/website/sidebars.json new file mode 100644 index 00000000..8e394773 --- /dev/null +++ b/website/sidebars.json @@ -0,0 +1,93 @@ +{ + "docs": { + "Basic": [ + "basic/setup", + { + "type": "subcategory", + "label": "Getting Started", + "ids": [ + "basic/getting-started/function_components", + "basic/getting-started/hooks", + "basic/getting-started/class_components", + "basic/getting-started/default_props", + "basic/getting-started/types_or_interfaces", + "basic/getting-started/basic_type_example", + "basic/getting-started/react_prop_type_example", + "basic/getting-started/get_derived_props_from_state", + "basic/getting-started/forms_and_events", + "basic/getting-started/context", + "basic/getting-started/forward_and_create_ref", + "basic/getting-started/portals", + "basic/getting-started/error_boundaries", + "basic/getting-started/concurrent" + ] + }, + { + "type": "subcategory", + "label": "Troubleshooting Handbook", + "ids": [ + "basic/troubleshooting/types", + "basic/troubleshooting/operators", + "basic/troubleshooting/utilities", + "basic/troubleshooting/non_ts_files", + "basic/troubleshooting/tsconfig", + "basic/troubleshooting/oficial_typings_bug" + ] + }, + { + "type": "subcategory", + "label": "Recommended React + TypeScript", + "ids": [ + "basic/recommended/resources", + "basic/recommended/talks", + "basic/recommended/other_resources" + ] + }, + "basic/editor_integration", + "basic/linting", + "basic/examples" + ], + "HOC": [ + "hoc/intro", + "hoc/full_example", + "hoc/react_hoc_docs", + "hoc/excluding_props" + ], + "Advanced": [ + "advanced/intro", + "advanced/utility_types", + { + "type": "subcategory", + "label": "Guides", + "ids": [ + "advanced/guides/render_props", + "advanced/guides/hocs", + "advanced/guides/conditionally_rendering", + "advanced/guides/polymorphic_components", + "advanced/guides/generic_components", + "advanced/guides/type_component_diff_props", + "advanced/guides/props_one_other_not_both", + "advanced/guides/props_optionally_pass_one", + "advanced/guides/omit_attr", + "advanced/guides/type_zoo", + "advanced/guides/extract_props", + "advanced/guides/handling-exceptions", + "advanced/guides/third_party_libs" + ] + }, + "advanced/patterns", + "advanced/misc_concerns", + "advanced/types_react_api" + ], + "Migration": [ + "migration/intro", + "migration/general_conversion_approaches", + "migration/js_docs", + "migration/from_js", + "migration/from_flow", + "migration/results", + "migration/academic_studies", + "migration/links" + ] + } +} diff --git a/website/siteConfig.js b/website/siteConfig.js new file mode 100644 index 00000000..5faac0db --- /dev/null +++ b/website/siteConfig.js @@ -0,0 +1,106 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// See https://docusaurus.io/docs/site-config for all the possible +// site configuration options. + +// List of projects/orgs using your project for the users page. +const users = [ + { + caption: "User1", + // You will need to prepend the image path with your baseUrl + // if it is not '/', like: '/test-site/img/image.jpg'. + image: "/img/undraw_open_source.svg", + infoLink: "https://www.facebook.com", + pinned: true, + }, +]; + +const siteConfig = { + title: "React TypeScript Cheatsheets", // Title for your website. + tagline: + "Cheatsheets for experienced React developers getting started with TypeScript", + /* TODO: Change this to official branch before gets merged */ + url: "https://raulfdm.github.io", // Your website URL + baseUrl: "/react-typescript-cheatsheet/", + /* TODO: Change this to official branch before gets merged */ + projectName: "react-typescript-cheatsheet", + organizationName: "raulfdm", + + // For no header links in the top nav bar -> headerLinks: [], + headerLinks: [ + { doc: "basic/setup", label: "Docs" }, + { page: "help", label: "Help" }, + // {blog: true, label: 'Blog'}, + ], + + // If you have users set above, you add it here: + users, + + /* path to images for header/footer */ + /* TODO: Make it smaller */ + headerIcon: "img/icon.png", + footerIcon: "img/icon.png", + favicon: "img/icon.png", + + /* Colors for website */ + colors: { + primaryColor: "#222222", + secondaryColor: "#171717", + }, + + /* Custom fonts for website */ + /* + fonts: { + myFont: [ + "Times New Roman", + "Serif" + ], + myOtherFont: [ + "-apple-system", + "system-ui" + ] + }, + */ + + // This copyright info is used in /core/Footer.js and blog RSS/Atom feeds. + copyright: `Copyright © ${new Date().getFullYear()} Your Name or Your Company Name`, + + highlight: { + // Highlight.js theme to use for syntax highlighting in code blocks. + theme: "default", + }, + + // Add custom scripts here that would be placed in