├── og-image.png ├── .gitignore ├── src ├── 01-introduction │ ├── 02-typescript-in-react-frameworks.explainer.ts │ ├── 01-react-in-typescript.explainer.tsx │ ├── 03-navigating-jsx-types.problem.tsx │ └── 03-navigating-jsx-types.solution.tsx ├── fake-external-lib │ ├── component.tsx │ └── index.ts ├── 03-hooks │ ├── 19-use-ref-with-elements.problem.tsx │ ├── 19-use-ref-with-elements.solution.tsx │ ├── 20-why-is-my-ref-readonly.problem.tsx │ ├── 17-use-ref-basics.problem.tsx │ ├── 14-use-effect.problem.ts │ ├── 17-use-ref-basics.solution.tsx │ ├── 14-use-effect.solution.ts │ ├── 20-why-is-my-ref-readonly.solution.tsx │ ├── 16-use-memo.solution.1.tsx │ ├── 16-use-memo.solution.2.tsx │ ├── 16-use-memo.solution.3.tsx │ ├── 16-use-memo.problem.tsx │ ├── 12-use-state-with-undefined.problem.ts │ ├── 11-use-state.problem.tsx │ ├── 12-use-state-with-undefined.solution.1.ts │ ├── 12-use-state-with-undefined.solution.2.ts │ ├── 15-use-callback.problem.tsx │ ├── 15-use-callback.solution.tsx │ ├── 11-use-state.solution.tsx │ ├── 18-element-refs.explainer.tsx │ ├── 21-use-reducer.problem.ts │ ├── 21-use-reducer.solution.1.ts │ ├── 21-use-reducer.solution.2.ts │ ├── 21-use-reducer.solution.3.ts │ ├── 13-use-state-excess-properties.problem.tsx │ └── 13-use-state-excess-properties.solution.tsx ├── 04-advanced-props │ ├── 26-empty-object-type.explainer.tsx │ ├── 31-as-const.problem.ts │ ├── discussion.md │ ├── 32-satisfies-vs-annotation-vs-as.problem.tsx │ ├── 31-as-const.solution.ts │ ├── 32-satisfies-vs-annotation-vs-as.solution.tsx │ ├── 22-discriminated-union-props.solution.tsx │ ├── 30-partial-autocomplete.solution.tsx │ ├── 27-either-all-these-props-or-none.solution.tsx │ ├── 23-destructuring-discriminated-unions.solution.1.tsx │ ├── 28-passing-react-components-vs-passing-react-nodes.solution.2.tsx │ ├── 23-destructuring-discriminated-unions.problem.tsx │ ├── 29-variants-with-classnames.solution.tsx │ ├── 23-destructuring-discriminated-unions.solution.2.tsx │ ├── 29-variants-with-classnames.problem.tsx │ ├── 22-discriminated-union-props.problem.tsx │ ├── 28-passing-react-components-vs-passing-react-nodes.solution.1.tsx │ ├── 25-toggle-props.solution.tsx │ ├── 28-passing-react-components-vs-passing-react-nodes.problem.tsx │ ├── 27-either-all-these-props-or-none.problem.tsx │ ├── 25-toggle-props.problem.tsx │ ├── 24-discriminated-union-with-other-props.problem.tsx │ ├── 24-discriminated-union-with-other-props.solution.tsx │ ├── 30-partial-autocomplete.problem.tsx │ ├── 33-prop-groups-with-variants.solution.tsx │ └── 33-prop-groups-with-variants.problem.tsx ├── 02-components │ ├── 08-using-html-props.problem.tsx │ ├── 06-typing-children.problem.tsx │ ├── 07-typing-onclick-handlers.problem.tsx │ ├── 06-typing-children.solution.tsx │ ├── 08-using-html-props.solution.2.tsx │ ├── 04-typing-components.problem.tsx │ ├── 04-typing-components.solution.3.tsx │ ├── 04-typing-components.solution.4.tsx │ ├── 05-typing-components-as-functions.problem.tsx │ ├── 07-typing-onclick-handlers.solution.tsx │ ├── 05-typing-components-as-functions.solution.tsx │ ├── 08-using-html-props.solution.1.tsx │ ├── 04-typing-components.solution.2.tsx │ ├── 04-typing-components.solution.1.tsx │ ├── 10-extracting-props-from-custom-components.problem.tsx │ ├── 10-extracting-props-from-custom-components.solution.tsx │ ├── 09-html-props-with-one-changed.problem.tsx │ ├── 09-html-props-with-one-changed.solution.2.tsx │ ├── 09-html-props-with-one-changed.solution.3.tsx │ ├── 09-html-props-with-one-changed.solution.1.tsx │ └── 09-html-props-with-one-changed.solution.4.tsx ├── 06-advanced-hooks │ ├── discussion.md │ ├── 45-tuple-return-type.solution.2.ts │ ├── 45-tuple-return-type.solution.1.ts │ ├── 50-use-state-overloads.solution.1.ts │ ├── 45-tuple-return-type.problem.ts │ ├── 50-use-state-overloads.solution.2.ts │ ├── 52-currying-hooks.solution.ts │ ├── 49-discriminated-tuples-from-custom-hooks.solution.tsx │ ├── 48-discriminated-unions-in-usestate.problem.tsx │ ├── 50-use-state-overloads.problem.ts │ ├── 52-currying-hooks.problem.ts │ ├── 51-function-overloads-in-hooks.problem.ts │ ├── 47-unions-in-usestate.solution.tsx │ ├── 51-function-overloads-in-hooks.solution.1.ts │ ├── 51-function-overloads-in-hooks.solution.2.ts │ ├── 48-discriminated-unions-in-usestate.solution.tsx │ ├── 46-required-context.solution.1.tsx │ ├── 46-required-context.solution.2.tsx │ ├── 49-discriminated-tuples-from-custom-hooks.problem.tsx │ ├── 47-unions-in-usestate.problem.tsx │ └── 46-required-context.problem.tsx ├── 05-generics │ ├── discussion.md │ ├── 36-type-helpers-with-constraints.solution.1.tsx │ ├── 36-type-helpers-with-constraints.solution.2.tsx │ ├── 34-type-helpers.problem.tsx │ ├── 36-type-helpers-with-constraints.problem.tsx │ ├── 38-generic-hooks.solution.5.ts │ ├── 34-type-helpers.solution.tsx │ ├── 38-generic-hooks.solution.2.ts │ ├── 38-generic-hooks.solution.3.ts │ ├── 38-generic-hooks.solution.4.ts │ ├── 35-type-helpers-2.solution.tsx │ ├── 37-generic-localstorage-hook.solution.ts │ ├── 38-generic-hooks.solution.1.ts │ ├── 38-generic-hooks.problem.ts │ ├── 42-generic-inference-through-multiple-helpers.solution.tsx │ ├── 35-type-helpers-2.problem.tsx │ ├── 40-generic-class-components.problem.tsx │ ├── 44-generics-vs-discriminated-unions.solution.tsx │ ├── 40-generic-class-components.solution.tsx │ ├── 41-passing-types-to-components.problem.tsx │ ├── 41-passing-types-to-components.solution.tsx │ ├── 39-generic-props.solution.tsx │ ├── 37-generic-localstorage-hook.problem.ts │ ├── 39-generic-props.problem.tsx │ ├── 42-generic-inference-through-multiple-helpers.problem.tsx │ ├── 44-generics-vs-discriminated-unions.problem.tsx │ ├── 43-inference-flow-with-constraints.problem.ts │ └── 43-inference-flow-with-constraints.solution.ts ├── 07-types-deep-dive │ ├── 58-appending-to-global-namespace.solution.ts │ ├── 60-add-new-global-element.solution.tsx │ ├── 62-add-attribute-to-all-elements.solution.tsx │ ├── 58-appending-to-global-namespace.problem.ts │ ├── 62-add-attribute-to-all-elements.problem.tsx │ ├── discussion.md │ ├── 53-understand-react-namespace-export.problem.ts │ ├── 53-understand-react-namespace-export.solution.ts │ ├── 61-html-attributes.problem.tsx │ ├── 61-html-attributes.solution.tsx │ ├── 60-add-new-global-element.problem.tsx │ ├── 59-declaration-merging-in-global-namespace.solution.ts │ ├── 59-declaration-merging-in-global-namespace.problem.ts │ ├── 57-react-component-type.explainer.tsx │ ├── 56-understanding-jsx-intrinsic-elements.problem.tsx │ ├── 56-understanding-jsx-intrinsic-elements.solution.tsx │ ├── 55-strongly-typing-children.problem.tsx │ ├── 55-strongly-typing-children.solution.tsx │ ├── 54-understanding-jsx-element.problem.tsx │ └── 54-understanding-jsx-element.solution.tsx ├── 08-advanced-patterns │ ├── discussion.md │ ├── 67-hoc.problem.tsx │ ├── 65-forward-ref-with-generics.explainer.2.tsx │ ├── 67-hoc.solution.tsx │ ├── 63-lazy-load-component.solution.tsx │ ├── 64.5-record-of-components-with-same-props.solution.1.tsx │ ├── 64-render-props.solution.tsx │ ├── 63-lazy-load-component.problem.tsx │ ├── 65-forward-ref-with-generics.explainer.3.tsx │ ├── 64.5-record-of-components-with-same-props.problem.tsx │ ├── 64.5-record-of-components-with-same-props.solution.2.tsx │ ├── 66-forward-ref-as-local-function.solution.1.tsx │ ├── 66-forward-ref-as-local-function.solution.2.tsx │ ├── 67.5-hoc-for-generic-components.problem.tsx │ ├── 66-forward-ref-as-local-function.problem.tsx │ ├── 69-as-prop.solution.2.tsx │ ├── 70-as-prop-with-custom-components.solution.tsx │ ├── 64-render-props.problem.tsx │ ├── 67.5-hoc-for-generic-components.solution.tsx │ ├── 69-as-prop.solution.1.tsx │ ├── 65-forward-ref-with-generics.explainer.1.tsx │ ├── 70-as-prop-with-custom-components.problem.tsx │ ├── 69-as-prop.problem.tsx │ ├── 71-as-prop-with-default.problem.tsx │ ├── 71-as-prop-with-default.solution.2.tsx │ ├── 71-as-prop-with-default.solution.1.tsx │ ├── 72-as-prop-with-forward-ref.problem.tsx │ └── 72-as-prop-with-forward-ref.solution.tsx ├── 09-external-libraries │ ├── discussion.md │ ├── 75-react-select.solution.tsx │ ├── 75-react-select.problem.tsx │ ├── 74-react-hook-form-wrapper.solution.1.tsx │ ├── 77-react-query-wrapper.solution.ts │ ├── 74-react-hook-form-wrapper.solution.2.tsx │ ├── 74-react-hook-form-wrapper.problem.tsx │ ├── 76-react-query.problem.ts │ ├── 76-react-query.solution.ts │ ├── 77-react-query-wrapper.problem.ts │ ├── 73-react-hook-form.problem.tsx │ └── 73-react-hook-form.solution.tsx └── helpers │ └── type-utils.ts ├── .vscode └── settings.json ├── vite.config.mts ├── renovate.json ├── tsconfig.json ├── .github └── workflows │ ├── section-repos.yml │ └── renovate-checks.yml └── README.md /og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/total-typescript/react-typescript-tutorial/HEAD/og-image.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | tsconfig.temp.json 3 | dist 4 | *.tsbuildinfo 5 | *.prompt.* 6 | .vscode/*.code-snippets -------------------------------------------------------------------------------- /src/01-introduction/02-typescript-in-react-frameworks.explainer.ts: -------------------------------------------------------------------------------- 1 | // Do a quick walkthrough of Vite, Next.js and Remix 2 | -------------------------------------------------------------------------------- /src/01-introduction/01-react-in-typescript.explainer.tsx: -------------------------------------------------------------------------------- 1 | // Explain how React works with TypeScript 2 | // tsconfig options: jsx: "preserve" 3 | // @types/react 4 | -------------------------------------------------------------------------------- /src/fake-external-lib/component.tsx: -------------------------------------------------------------------------------- 1 | const MyComponent = (props: { id: string }) => { 2 | return
Hello!
; 3 | }; 4 | 5 | export default MyComponent; 6 | -------------------------------------------------------------------------------- /src/03-hooks/19-use-ref-with-elements.problem.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const ref = useRef(); 5 | 6 | return
; 7 | }; 8 | -------------------------------------------------------------------------------- /src/03-hooks/19-use-ref-with-elements.solution.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const ref = useRef(null); 5 | 6 | return
; 7 | }; 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules/typescript/lib", 3 | "typescript.enablePromptUseWorkspaceTsdk": true, 4 | "github.copilot.enable": { 5 | "*": false, 6 | }, 7 | "explorer.sortOrder": "mixed", 8 | } -------------------------------------------------------------------------------- /src/03-hooks/20-why-is-my-ref-readonly.problem.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const ref = useRef(null); 5 | 6 | // Why is this not allowed? 7 | ref.current = "Hello"; 8 | 9 | return null; 10 | }; 11 | -------------------------------------------------------------------------------- /src/03-hooks/17-use-ref-basics.problem.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const id = useRef(); 5 | 6 | useEffect(() => { 7 | id.current = "Random value!"; 8 | }, []); 9 | 10 | return
; 11 | }; 12 | -------------------------------------------------------------------------------- /src/03-hooks/14-use-effect.problem.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | export const useTimeout = (timerMs: number) => { 4 | useEffect( 5 | () => 6 | setTimeout(() => { 7 | console.log("Done!"); 8 | }, timerMs), 9 | [timerMs], 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /src/03-hooks/17-use-ref-basics.solution.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const id = useRef(); 5 | 6 | useEffect(() => { 7 | id.current = "Random value!"; 8 | }, []); 9 | 10 | return
; 11 | }; 12 | -------------------------------------------------------------------------------- /src/04-advanced-props/26-empty-object-type.explainer.tsx: -------------------------------------------------------------------------------- 1 | const Component = (props: { config: {} }) => { 2 | return
; 3 | }; 4 | 5 | /** 6 | * Why can I pass _anything_ to config? 7 | */ 8 | <> 9 | 15 | ; 16 | -------------------------------------------------------------------------------- /vite.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import tsconfigPaths from "vite-tsconfig-paths"; 3 | 4 | export default defineConfig({ 5 | test: { 6 | include: ["src/**/*{problem,solution,explainer}*.{ts,tsx}"], 7 | passWithNoTests: true, 8 | environment: "jsdom", 9 | }, 10 | plugins: [tsconfigPaths()], 11 | }); 12 | -------------------------------------------------------------------------------- /src/02-components/08-using-html-props.problem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = ({ className, ...rest }: {}) => { 4 | return ( 5 | 6 | ); 7 | }; 8 | 9 | const Parent = () => { 10 | return ; 11 | }; 12 | -------------------------------------------------------------------------------- /src/03-hooks/14-use-effect.solution.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | 3 | export const useTimeout = (timerMs: number) => { 4 | useEffect(() => { 5 | const timeout = setTimeout(() => { 6 | console.log("Done!"); 7 | }, timerMs); 8 | 9 | return () => { 10 | clearTimeout(timeout); 11 | }; 12 | }, [timerMs]); 13 | }; 14 | -------------------------------------------------------------------------------- /src/02-components/06-typing-children.problem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = (props: {}) => { 4 | return ; 5 | }; 6 | 7 | const Parent = () => { 8 | return ( 9 | <> 10 | {/* @ts-expect-error */} 11 | 12 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/06-advanced-hooks/discussion.md: -------------------------------------------------------------------------------- 1 | What use cases have you seen for function overloads that aren't related to React? 2 | 3 | How are function overloads different to generics? Which are safer? Which are more flexible? 4 | 5 | Which do you prefer - discriminated tuples or discriminated unions of objects? Why? 6 | 7 | Have you used 'as const' before? If so, what was your use case? 8 | -------------------------------------------------------------------------------- /src/01-introduction/03-navigating-jsx-types.problem.tsx: -------------------------------------------------------------------------------- 1 | export const Component = () => { 2 | return ( 3 |
11 | ); 12 | }; 13 | -------------------------------------------------------------------------------- /src/01-introduction/03-navigating-jsx-types.solution.tsx: -------------------------------------------------------------------------------- 1 | export const Component = () => { 2 | return ( 3 |
{}} // React.FormEventHandler | undefined 6 | 7 | // How do I get autocomplete with JSX: ctrl-space 8 | /> 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /src/02-components/07-typing-onclick-handlers.problem.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface ButtonProps { 4 | className: string; 5 | children: React.ReactNode; 6 | } 7 | 8 | export const Button = ({ children, className, onClick }: ButtonProps) => { 9 | return ( 10 | 13 | ); 14 | }; 15 | -------------------------------------------------------------------------------- /src/02-components/06-typing-children.solution.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = (props: { children: React.ReactNode }) => { 4 | return ; 5 | }; 6 | 7 | const Parent = () => { 8 | return ( 9 | <> 10 | {/* @ts-expect-error */} 11 | 12 | 13 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/02-components/08-using-html-props.solution.2.tsx: -------------------------------------------------------------------------------- 1 | import React, { ComponentProps } from "react"; 2 | 3 | export const Button = ({ className, ...rest }: ComponentProps<"button">) => { 4 | return ( 5 | 6 | ); 7 | }; 8 | 9 | const Parent = () => { 10 | return ; 11 | }; 12 | -------------------------------------------------------------------------------- /src/02-components/04-typing-components.problem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = (props: unknown) => { 4 | return ; 5 | }; 6 | 7 | const Parent = () => { 8 | return ( 9 | <> 10 | {/* @ts-expect-error */} 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/05-generics/discussion.md: -------------------------------------------------------------------------------- 1 | Why doesn't this code work without the comma (in a .tsx file)? 2 | 3 | ```tsx 4 | const Component = () => {}; 5 | ``` 6 | 7 | What problems have you solved with generics that aren't related to React? 8 | 9 | What libraries can you think of that rely on generics to do their magic? 10 | 11 | What use cases have you seen for `React.ComponentType` and `React.ComponentProps`? 12 | -------------------------------------------------------------------------------- /src/07-types-deep-dive/58-appending-to-global-namespace.solution.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "../helpers/type-utils"; 2 | 3 | declare global { 4 | namespace React { 5 | // Note that it doesn't need to be exported 6 | interface MySolutionInterface { 7 | foo: string; 8 | } 9 | } 10 | } 11 | 12 | type test = Expect>; 13 | 14 | export {}; 15 | -------------------------------------------------------------------------------- /src/02-components/04-typing-components.solution.3.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = (props: { className: string }) => { 4 | return ; 5 | }; 6 | 7 | const Parent = () => { 8 | return ( 9 | <> 10 | {/* @ts-expect-error */} 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/02-components/04-typing-components.solution.4.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | export const Button = ({ className }: { className: string }) => { 4 | return ; 5 | }; 6 | 7 | const Parent = () => { 8 | return ( 9 | <> 10 | {/* @ts-expect-error */} 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /src/02-components/05-typing-components-as-functions.problem.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface Props { 4 | className: string; 5 | } 6 | 7 | /* @ts-expect-error */ 8 | export const Button = (props: Props) => { 9 | return { 10 | ohDear: "123", 11 | }; 12 | }; 13 | 14 | const Parent = () => { 15 | return ( 16 | <> 17 | 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/03-hooks/20-why-is-my-ref-readonly.solution.tsx: -------------------------------------------------------------------------------- 1 | import { useRef } from "react"; 2 | 3 | export const Component = () => { 4 | const firstOverload = useRef("124123123"); 5 | 6 | firstOverload.current = "123j12jhb123jhb"; 7 | 8 | const secondOverload = useRef(null); 9 | 10 | secondOverload.current = "Hello"; 11 | 12 | const thirdOverload = useRef(); 13 | 14 | return null; 15 | }; 16 | -------------------------------------------------------------------------------- /src/02-components/07-typing-onclick-handlers.solution.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface ButtonProps { 4 | className: string; 5 | children: React.ReactNode; 6 | onClick: React.MouseEventHandler; 7 | } 8 | 9 | export const Button = ({ children, className, onClick }: ButtonProps) => { 10 | return ( 11 | 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /src/02-components/05-typing-components-as-functions.solution.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | interface Props { 4 | className: string; 5 | } 6 | 7 | /* @ts-expect-error */ 8 | export const Button: React.FC = (props: Props) => { 9 | return { 10 | ohDear: "123", 11 | }; 12 | }; 13 | 14 | const Parent = () => { 15 | return ( 16 | <> 17 | 18 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/02-components/08-using-html-props.solution.1.tsx: -------------------------------------------------------------------------------- 1 | import React, { ButtonHTMLAttributes } from "react"; 2 | 3 | export const Button = ({ 4 | className, 5 | ...rest 6 | }: ButtonHTMLAttributes) => { 7 | return ( 8 | 9 | ); 10 | }; 11 | 12 | const Parent = () => { 13 | return ; 14 | }; 15 | -------------------------------------------------------------------------------- /src/04-advanced-props/31-as-const.problem.ts: -------------------------------------------------------------------------------- 1 | import { Equal, Expect } from "../helpers/type-utils"; 2 | 3 | const BACKEND_TO_FRONTEND_STATUS_MAP = { 4 | 0: "pending", 5 | 1: "success", 6 | 2: "error", 7 | }; 8 | 9 | type BackendStatus = unknown; 10 | type FrontendStatus = unknown; 11 | 12 | type test = [ 13 | Expect>, 14 | Expect> 15 | ]; 16 | -------------------------------------------------------------------------------- /src/07-types-deep-dive/60-add-new-global-element.solution.tsx: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace JSX { 3 | interface IntrinsicElements { 4 | "something-solution": { 5 | id: string; 6 | }; 7 | } 8 | } 9 | } 10 | 11 | <> 12 | 13 | 14 | {/* @ts-expect-error */} 15 | 16 | 17 | {/* @ts-expect-error */} 18 | 19 | ; 20 | -------------------------------------------------------------------------------- /src/04-advanced-props/discussion.md: -------------------------------------------------------------------------------- 1 | What's the most common issue you see with the way declare prop types in React? 2 | 3 | Have you used a discriminated union before? If so, what was your use case? 4 | 5 | What problems could you imagine solving with discriminated unions that AREN'T related to React? 6 | 7 | What use cases have you seen for the `satisfies` operator? 8 | 9 | What's the strangest type error you've ever seen in a React app? 10 | -------------------------------------------------------------------------------- /src/07-types-deep-dive/62-add-attribute-to-all-elements.solution.tsx: -------------------------------------------------------------------------------- 1 | declare global { 2 | namespace React { 3 | interface HTMLAttributes { 4 | solutionTestId?: string; 5 | } 6 | } 7 | } 8 | 9 | <> 10 |
11 |