From 1b708f67c21b6afe35902ceb9a6840bd1913d1d5 Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Wed, 31 May 2023 14:27:30 +0100 Subject: [PATCH 001/165] Added 27, modified 26 --- .../26-partial-autocomplete.problem.tsx | 29 +++++++++++- .../26-partial-autocomplete.solution.1.tsx | 27 +++++++++++ .../26-partial-autocomplete.solution.2.tsx | 29 ++++++++++++ ...-of-components-with-same-props.problem.tsx | 45 +++++++++++++++++++ ...-components-with-same-props.solution.1.tsx | 40 +++++++++++++++++ ...-components-with-same-props.solution.2.tsx | 44 ++++++++++++++++++ ...onents-vs-passing-react-nodes.problem.tsx} | 0 ...nts-vs-passing-react-nodes.solution.1.tsx} | 0 ...nts-vs-passing-react-nodes.solution.2.tsx} | 0 ...oblem.tsx => 29-generic-props.problem.tsx} | 0 ...tion.tsx => 29-generic-props.solution.tsx} | 0 ...erics-vs-discriminated-unions.problem.tsx} | 0 ...rics-vs-discriminated-unions.solution.tsx} | 0 ...> 31-variants-with-classnames.problem.tsx} | 0 ... 31-variants-with-classnames.solution.tsx} | 0 ... 32-prop-groups-with-variants.problem.tsx} | 0 ...-prop-groups-with-variants.solution.1.tsx} | 0 ...-prop-groups-with-variants.solution.2.tsx} | 0 .../32-typing-custom-hooks.problem.ts | 10 ----- .../32-typing-custom-hooks.solution.1.ts | 10 ----- .../32-typing-custom-hooks.solution.2.ts | 10 ----- .../32-typing-custom-hooks.solution.3.ts | 10 ----- ...-of-components-with-same-props.problem.tsx | 1 - ...lem.tsx => 59-react-hook-form.problem.tsx} | 0 ...problem.tsx => 60-react-query.problem.tsx} | 0 ...oblem.tsx => 61-redux-toolkit.problem.tsx} | 0 ...and.problem.tsx => 62-zustand.problem.tsx} | 0 ...eusable-form-library-with-zod.problem.tsx} | 0 28 files changed, 213 insertions(+), 42 deletions(-) create mode 100644 src/04-advanced-components/26-partial-autocomplete.solution.1.tsx create mode 100644 src/04-advanced-components/26-partial-autocomplete.solution.2.tsx create mode 100644 src/04-advanced-components/27-record-of-components-with-same-props.problem.tsx create mode 100644 src/04-advanced-components/27-record-of-components-with-same-props.solution.1.tsx create mode 100644 src/04-advanced-components/27-record-of-components-with-same-props.solution.2.tsx rename src/04-advanced-components/{27-passing-react-components-vs-passing-react-nodes.problem.tsx => 28-passing-react-components-vs-passing-react-nodes.problem.tsx} (100%) rename src/04-advanced-components/{27-passing-react-components-vs-passing-react-nodes.solution.1.tsx => 28-passing-react-components-vs-passing-react-nodes.solution.1.tsx} (100%) rename src/04-advanced-components/{27-passing-react-components-vs-passing-react-nodes.solution.2.tsx => 28-passing-react-components-vs-passing-react-nodes.solution.2.tsx} (100%) rename src/04-advanced-components/{28-generic-props.problem.tsx => 29-generic-props.problem.tsx} (100%) rename src/04-advanced-components/{28-generic-props.solution.tsx => 29-generic-props.solution.tsx} (100%) rename src/04-advanced-components/{29-generics-vs-discriminated-unions.problem.tsx => 30-generics-vs-discriminated-unions.problem.tsx} (100%) rename src/04-advanced-components/{29-generics-vs-discriminated-unions.solution.tsx => 30-generics-vs-discriminated-unions.solution.tsx} (100%) rename src/04-advanced-components/{30-variants-with-classnames.problem.tsx => 31-variants-with-classnames.problem.tsx} (100%) rename src/04-advanced-components/{30-variants-with-classnames.solution.tsx => 31-variants-with-classnames.solution.tsx} (100%) rename src/04-advanced-components/{31-prop-groups-with-variants.problem.tsx => 32-prop-groups-with-variants.problem.tsx} (100%) rename src/04-advanced-components/{31-prop-groups-with-variants.solution.1.tsx => 32-prop-groups-with-variants.solution.1.tsx} (100%) rename src/04-advanced-components/{31-prop-groups-with-variants.solution.2.tsx => 32-prop-groups-with-variants.solution.2.tsx} (100%) delete mode 100644 src/05-advanced-hooks/32-typing-custom-hooks.problem.ts delete mode 100644 src/05-advanced-hooks/32-typing-custom-hooks.solution.1.ts delete mode 100644 src/05-advanced-hooks/32-typing-custom-hooks.solution.2.ts delete mode 100644 src/05-advanced-hooks/32-typing-custom-hooks.solution.3.ts delete mode 100644 src/07-advanced-patterns/59-record-of-components-with-same-props.problem.tsx rename src/08-external-libraries/{60-react-hook-form.problem.tsx => 59-react-hook-form.problem.tsx} (100%) rename src/08-external-libraries/{61-react-query.problem.tsx => 60-react-query.problem.tsx} (100%) rename src/08-external-libraries/{62-redux-toolkit.problem.tsx => 61-redux-toolkit.problem.tsx} (100%) rename src/08-external-libraries/{63-zustand.problem.tsx => 62-zustand.problem.tsx} (100%) rename src/08-external-libraries/{64-reusable-form-library-with-zod.problem.tsx => 63-reusable-form-library-with-zod.problem.tsx} (100%) diff --git a/src/04-advanced-components/26-partial-autocomplete.problem.tsx b/src/04-advanced-components/26-partial-autocomplete.problem.tsx index 70b786d..6248a9b 100644 --- a/src/04-advanced-components/26-partial-autocomplete.problem.tsx +++ b/src/04-advanced-components/26-partial-autocomplete.problem.tsx @@ -1 +1,28 @@ -// TODO +const presetSizes = { + xs: "0.5rem", + sm: "1rem", +}; + +type Size = keyof typeof presetSizes; + +type LooseSize = Size | string; + +export const Icon = (props: { size: LooseSize }) => { + return ( +
+ ); +}; + +<> + {/* Autocomplete for sm and xs are no longer working! */} + + + +; diff --git a/src/04-advanced-components/26-partial-autocomplete.solution.1.tsx b/src/04-advanced-components/26-partial-autocomplete.solution.1.tsx new file mode 100644 index 0000000..01751e1 --- /dev/null +++ b/src/04-advanced-components/26-partial-autocomplete.solution.1.tsx @@ -0,0 +1,27 @@ +const presetSizes = { + xs: "0.5rem", + sm: "1rem", +}; + +type Size = keyof typeof presetSizes; + +type LooseSize = Size | (string & {}); + +export const Icon = (props: { size: LooseSize }) => { + return ( +
+ ); +}; + +<> + + + +; diff --git a/src/04-advanced-components/26-partial-autocomplete.solution.2.tsx b/src/04-advanced-components/26-partial-autocomplete.solution.2.tsx new file mode 100644 index 0000000..9d15d7d --- /dev/null +++ b/src/04-advanced-components/26-partial-autocomplete.solution.2.tsx @@ -0,0 +1,29 @@ +const presetSizes = { + xs: "0.5rem", + sm: "1rem", +}; + +type Size = keyof typeof presetSizes; + +type LooseAutocomplete = T | (string & {}); + +type LooseSize = LooseAutocomplete; + +export const Icon = (props: { size: LooseSize }) => { + return ( +
+ ); +}; + +<> + + + +; diff --git a/src/04-advanced-components/27-record-of-components-with-same-props.problem.tsx b/src/04-advanced-components/27-record-of-components-with-same-props.problem.tsx new file mode 100644 index 0000000..8a10719 --- /dev/null +++ b/src/04-advanced-components/27-record-of-components-with-same-props.problem.tsx @@ -0,0 +1,45 @@ +import { Equal, Expect } from "../helpers/type-utils"; + +type InputProps = React.ComponentProps<"input">; + +/** + * All these components take the same props! + * + * We don't want to repeat ourselves by typing + * props: InputProps for each component. + * + * There must be a better way! + * + * Hint: Record and satisfies will come in handy. + */ +const COMPONENTS = { + text: (props) => { + return ; + }, + number: (props) => { + return ; + }, + password: (props) => { + return ; + }, +}; + +export const Input = (props: unknown) => { + const Component = COMPONENTS[props.type]; + return ; +}; + +<> + { + // e should be properly typed! + type test = Expect>>; + }} + > + + + + {/* @ts-expect-error */} + +; diff --git a/src/04-advanced-components/27-record-of-components-with-same-props.solution.1.tsx b/src/04-advanced-components/27-record-of-components-with-same-props.solution.1.tsx new file mode 100644 index 0000000..79c1baa --- /dev/null +++ b/src/04-advanced-components/27-record-of-components-with-same-props.solution.1.tsx @@ -0,0 +1,40 @@ +import { Equal, Expect } from "../helpers/type-utils"; + +type InputProps = React.ComponentProps<"input">; + +type Input = "text" | "number" | "password"; + +/** + * We can do it by typing Input and making COMPONENTS + * restricted to only those inputs. + */ +const COMPONENTS: Record> = { + text: (props) => { + return ; + }, + number: (props) => { + return ; + }, + password: (props) => { + return ; + }, +}; + +export const Input = (props: { type: Input } & InputProps) => { + const Component = COMPONENTS[props.type]; + return ; +}; + +<> + { + type test = Expect>>; + }} + > + + + + {/* @ts-expect-error */} + +; diff --git a/src/04-advanced-components/27-record-of-components-with-same-props.solution.2.tsx b/src/04-advanced-components/27-record-of-components-with-same-props.solution.2.tsx new file mode 100644 index 0000000..a1c689b --- /dev/null +++ b/src/04-advanced-components/27-record-of-components-with-same-props.solution.2.tsx @@ -0,0 +1,44 @@ +import { Equal, Expect } from "../helpers/type-utils"; + +type InputProps = React.ComponentProps<"input">; + +/** + * OR, we can do it by making COMPONENTS 'satisfy' + * a type that is a Record of React.FC. + */ +const COMPONENTS = { + text: (props) => { + return ; + }, + number: (props) => { + return ; + }, + password: (props) => { + return ; + }, +} satisfies Record>; + +/** + * Then, we can derive the type of input from the + * keys of COMPONENTS. + */ +type Input = keyof typeof COMPONENTS; + +export const Input = (props: { type: Input } & InputProps) => { + const Component = COMPONENTS[props.type]; + return ; +}; + +<> + { + type test = Expect>>; + }} + > + + + + {/* @ts-expect-error */} + +; diff --git a/src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.problem.tsx b/src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.problem.tsx similarity index 100% rename from src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.problem.tsx rename to src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.problem.tsx diff --git a/src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.solution.1.tsx b/src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.solution.1.tsx similarity index 100% rename from src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.solution.1.tsx rename to src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.solution.1.tsx diff --git a/src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.solution.2.tsx b/src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.solution.2.tsx similarity index 100% rename from src/04-advanced-components/27-passing-react-components-vs-passing-react-nodes.solution.2.tsx rename to src/04-advanced-components/28-passing-react-components-vs-passing-react-nodes.solution.2.tsx diff --git a/src/04-advanced-components/28-generic-props.problem.tsx b/src/04-advanced-components/29-generic-props.problem.tsx similarity index 100% rename from src/04-advanced-components/28-generic-props.problem.tsx rename to src/04-advanced-components/29-generic-props.problem.tsx diff --git a/src/04-advanced-components/28-generic-props.solution.tsx b/src/04-advanced-components/29-generic-props.solution.tsx similarity index 100% rename from src/04-advanced-components/28-generic-props.solution.tsx rename to src/04-advanced-components/29-generic-props.solution.tsx diff --git a/src/04-advanced-components/29-generics-vs-discriminated-unions.problem.tsx b/src/04-advanced-components/30-generics-vs-discriminated-unions.problem.tsx similarity index 100% rename from src/04-advanced-components/29-generics-vs-discriminated-unions.problem.tsx rename to src/04-advanced-components/30-generics-vs-discriminated-unions.problem.tsx diff --git a/src/04-advanced-components/29-generics-vs-discriminated-unions.solution.tsx b/src/04-advanced-components/30-generics-vs-discriminated-unions.solution.tsx similarity index 100% rename from src/04-advanced-components/29-generics-vs-discriminated-unions.solution.tsx rename to src/04-advanced-components/30-generics-vs-discriminated-unions.solution.tsx diff --git a/src/04-advanced-components/30-variants-with-classnames.problem.tsx b/src/04-advanced-components/31-variants-with-classnames.problem.tsx similarity index 100% rename from src/04-advanced-components/30-variants-with-classnames.problem.tsx rename to src/04-advanced-components/31-variants-with-classnames.problem.tsx diff --git a/src/04-advanced-components/30-variants-with-classnames.solution.tsx b/src/04-advanced-components/31-variants-with-classnames.solution.tsx similarity index 100% rename from src/04-advanced-components/30-variants-with-classnames.solution.tsx rename to src/04-advanced-components/31-variants-with-classnames.solution.tsx diff --git a/src/04-advanced-components/31-prop-groups-with-variants.problem.tsx b/src/04-advanced-components/32-prop-groups-with-variants.problem.tsx similarity index 100% rename from src/04-advanced-components/31-prop-groups-with-variants.problem.tsx rename to src/04-advanced-components/32-prop-groups-with-variants.problem.tsx diff --git a/src/04-advanced-components/31-prop-groups-with-variants.solution.1.tsx b/src/04-advanced-components/32-prop-groups-with-variants.solution.1.tsx similarity index 100% rename from src/04-advanced-components/31-prop-groups-with-variants.solution.1.tsx rename to src/04-advanced-components/32-prop-groups-with-variants.solution.1.tsx diff --git a/src/04-advanced-components/31-prop-groups-with-variants.solution.2.tsx b/src/04-advanced-components/32-prop-groups-with-variants.solution.2.tsx similarity index 100% rename from src/04-advanced-components/31-prop-groups-with-variants.solution.2.tsx rename to src/04-advanced-components/32-prop-groups-with-variants.solution.2.tsx diff --git a/src/05-advanced-hooks/32-typing-custom-hooks.problem.ts b/src/05-advanced-hooks/32-typing-custom-hooks.problem.ts deleted file mode 100644 index fc1144b..0000000 --- a/src/05-advanced-hooks/32-typing-custom-hooks.problem.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useState } from "react"; -import { Equal, Expect } from "../helpers/type-utils"; - -export const useId = (defaultId) => { - const [id] = useState(defaultId); - - return id; -}; - -type tests = [Expect string>>]; diff --git a/src/05-advanced-hooks/32-typing-custom-hooks.solution.1.ts b/src/05-advanced-hooks/32-typing-custom-hooks.solution.1.ts deleted file mode 100644 index cc51090..0000000 --- a/src/05-advanced-hooks/32-typing-custom-hooks.solution.1.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useState } from "react"; -import { Equal, Expect } from "../helpers/type-utils"; - -export const useId = (defaultId: string) => { - const [id] = useState(defaultId); - - return id; -}; - -type tests = [Expect string>>]; diff --git a/src/05-advanced-hooks/32-typing-custom-hooks.solution.2.ts b/src/05-advanced-hooks/32-typing-custom-hooks.solution.2.ts deleted file mode 100644 index 35f4be4..0000000 --- a/src/05-advanced-hooks/32-typing-custom-hooks.solution.2.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useState } from "react"; -import { Equal, Expect } from "../helpers/type-utils"; - -export const useId = (defaultId: string): string => { - const [id] = useState(defaultId); - - return id; -}; - -type tests = [Expect string>>]; diff --git a/src/05-advanced-hooks/32-typing-custom-hooks.solution.3.ts b/src/05-advanced-hooks/32-typing-custom-hooks.solution.3.ts deleted file mode 100644 index 0f90c81..0000000 --- a/src/05-advanced-hooks/32-typing-custom-hooks.solution.3.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { useState } from "react"; -import { Equal, Expect } from "../helpers/type-utils"; - -export const useId = (defaultId: string): string => { - const [id] = useState(defaultId); - - return id; -}; - -type tests = [Expect string>>]; diff --git a/src/07-advanced-patterns/59-record-of-components-with-same-props.problem.tsx b/src/07-advanced-patterns/59-record-of-components-with-same-props.problem.tsx deleted file mode 100644 index 9289714..0000000 --- a/src/07-advanced-patterns/59-record-of-components-with-same-props.problem.tsx +++ /dev/null @@ -1 +0,0 @@ -// https://codesandbox.io/s/young-dream-ihjcq3?file=/src/App.tsx diff --git a/src/08-external-libraries/60-react-hook-form.problem.tsx b/src/08-external-libraries/59-react-hook-form.problem.tsx similarity index 100% rename from src/08-external-libraries/60-react-hook-form.problem.tsx rename to src/08-external-libraries/59-react-hook-form.problem.tsx diff --git a/src/08-external-libraries/61-react-query.problem.tsx b/src/08-external-libraries/60-react-query.problem.tsx similarity index 100% rename from src/08-external-libraries/61-react-query.problem.tsx rename to src/08-external-libraries/60-react-query.problem.tsx diff --git a/src/08-external-libraries/62-redux-toolkit.problem.tsx b/src/08-external-libraries/61-redux-toolkit.problem.tsx similarity index 100% rename from src/08-external-libraries/62-redux-toolkit.problem.tsx rename to src/08-external-libraries/61-redux-toolkit.problem.tsx diff --git a/src/08-external-libraries/63-zustand.problem.tsx b/src/08-external-libraries/62-zustand.problem.tsx similarity index 100% rename from src/08-external-libraries/63-zustand.problem.tsx rename to src/08-external-libraries/62-zustand.problem.tsx diff --git a/src/08-external-libraries/64-reusable-form-library-with-zod.problem.tsx b/src/08-external-libraries/63-reusable-form-library-with-zod.problem.tsx similarity index 100% rename from src/08-external-libraries/64-reusable-form-library-with-zod.problem.tsx rename to src/08-external-libraries/63-reusable-form-library-with-zod.problem.tsx From 846c613130c6eb25f0154ec57f952183ecf51ece Mon Sep 17 00:00:00 2001 From: Matt Pocock Date: Wed, 31 May 2023 14:55:33 +0100 Subject: [PATCH 002/165] Added toggle props --- .../29-toggle-props.problem.tsx | 35 +++++++++++++++++++ .../29-toggle-props.solution.tsx | 32 +++++++++++++++++ ...oblem.tsx => 30-generic-props.problem.tsx} | 0 ...tion.tsx => 30-generic-props.solution.tsx} | 0 ...erics-vs-discriminated-unions.problem.tsx} | 0 ...rics-vs-discriminated-unions.solution.tsx} | 0 ...> 32-variants-with-classnames.problem.tsx} | 0 ... 32-variants-with-classnames.solution.tsx} | 0 ... 33-prop-groups-with-variants.problem.tsx} | 0 ...-prop-groups-with-variants.solution.1.tsx} | 0 ...-prop-groups-with-variants.solution.2.tsx} | 0 ...criminated-unions-with-booleans.problem.ts | 1 - ...lem.ts => 34-tuple-return-type.problem.ts} | 0 ....ts => 34-tuple-return-type.solution.1.ts} | 0 ....ts => 34-tuple-return-type.solution.2.ts} | 0 15 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 src/04-advanced-components/29-toggle-props.problem.tsx create mode 100644 src/04-advanced-components/29-toggle-props.solution.tsx rename src/04-advanced-components/{29-generic-props.problem.tsx => 30-generic-props.problem.tsx} (100%) rename src/04-advanced-components/{29-generic-props.solution.tsx => 30-generic-props.solution.tsx} (100%) rename src/04-advanced-components/{30-generics-vs-discriminated-unions.problem.tsx => 31-generics-vs-discriminated-unions.problem.tsx} (100%) rename src/04-advanced-components/{30-generics-vs-discriminated-unions.solution.tsx => 31-generics-vs-discriminated-unions.solution.tsx} (100%) rename src/04-advanced-components/{31-variants-with-classnames.problem.tsx => 32-variants-with-classnames.problem.tsx} (100%) rename src/04-advanced-components/{31-variants-with-classnames.solution.tsx => 32-variants-with-classnames.solution.tsx} (100%) rename src/04-advanced-components/{32-prop-groups-with-variants.problem.tsx => 33-prop-groups-with-variants.problem.tsx} (100%) rename src/04-advanced-components/{32-prop-groups-with-variants.solution.1.tsx => 33-prop-groups-with-variants.solution.1.tsx} (100%) rename src/04-advanced-components/{32-prop-groups-with-variants.solution.2.tsx => 33-prop-groups-with-variants.solution.2.tsx} (100%) delete mode 100644 src/05-advanced-hooks/34-discriminated-unions-with-booleans.problem.ts rename src/05-advanced-hooks/{33-tuple-return-type.problem.ts => 34-tuple-return-type.problem.ts} (100%) rename src/05-advanced-hooks/{33-tuple-return-type.solution.1.ts => 34-tuple-return-type.solution.1.ts} (100%) rename src/05-advanced-hooks/{33-tuple-return-type.solution.2.ts => 34-tuple-return-type.solution.2.ts} (100%) diff --git a/src/04-advanced-components/29-toggle-props.problem.tsx b/src/04-advanced-components/29-toggle-props.problem.tsx new file mode 100644 index 0000000..0cf3e34 --- /dev/null +++ b/src/04-advanced-components/29-toggle-props.problem.tsx @@ -0,0 +1,35 @@ +type EmbeddedPlaygroundProps = + | { + useStackblitz: true; + stackblitzId: string; + } + | { + useStackblitz?: false; + codeSandboxId: string; + }; + +const EmbeddedPlayground = (props: EmbeddedPlaygroundProps) => { + if (props.useStackblitz) { + return ( +