From 1f879ec04c249a149fa89e8736b1684ebd0c46e3 Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Mon, 8 Mar 2021 17:24:33 -0300 Subject: [PATCH 01/11] add react props & typescript standar and examples --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 6758e66..d20b24e 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ 4. [Formatting](#formatting) 5. [Comments](#comments) 6. [TypeScript](#typescript) + 1. [React props & TypeScript](#react-props-typescript) 7. [GIT](#git) 1. [Branches](#branches) 2. [Commit messages](#commit-messages) @@ -355,6 +356,38 @@ writable. Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible to modify the Array. +### React props & TypeScript + +`interface` is preferred over `type` when defining the component props. Are a little more powerful +than the corresponding type declaration, but for a set of react props, it likely doesn't matter. + +Also is not necessary to type the `returnType` because is inferred from what you actually return, so +can be omitted most of the time. + +So the best approach and example would be something like this: + +``` +interface FancyButtonProps { + color: string +} + +const FancyButton = (props: FancyButtonProps) => { + ... +} +``` + +or if you're using props destructuring: + +``` +interface FancyButtonProps { + color: string +} + +const FancyButton = ({ color }: FancyButtonProps) => { + ... +} +``` + ## GIT ### Branches From 4a1de3346b0f13991f82cf9affcf948a26df18c5 Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Mon, 8 Mar 2021 18:25:26 -0300 Subject: [PATCH 02/11] fix typos --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d20b24e..f72b1de 100644 --- a/README.md +++ b/README.md @@ -361,7 +361,7 @@ Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible `interface` is preferred over `type` when defining the component props. Are a little more powerful than the corresponding type declaration, but for a set of react props, it likely doesn't matter. -Also is not necessary to type the `returnType` because is inferred from what you actually return, so +Also it is not necessary to type the `returnType` because it is inferred from what you actually return, so can be omitted most of the time. So the best approach and example would be something like this: From 0019d3e2eed0be09e09216b08c177cf3c0d065e3 Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Wed, 17 Mar 2021 17:42:51 -0300 Subject: [PATCH 03/11] fix types and add some more examples --- README.md | 69 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 53 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index f72b1de..08c3d6f 100644 --- a/README.md +++ b/README.md @@ -358,34 +358,71 @@ Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible ### React props & TypeScript -`interface` is preferred over `type` when defining the component props. Are a little more powerful -than the corresponding type declaration, but for a set of react props, it likely doesn't matter. +#### Why use Interface instead of Type for typing props? -Also it is not necessary to type the `returnType` because it is inferred from what you actually return, so -can be omitted most of the time. +`interface` are a little more powerful than the corresponding `type` declaration. -So the best approach and example would be something like this: +One of the reasons to use `interface` is when you need to compose two or more types, you have the +option of extending those types with an interface, or intersecting them in a type alias. TS team +says: -``` -interface FancyButtonProps { - color: string -} +> Interfaces create a single flat object type that detects property conflicts, which are usually +> important to resolve! Intersections on the other hand just recursively merge properties, and in +> some cases produce never. Interfaces also display consistently better, whereas type aliases to +> intersections can't be displayed in part of other intersections. Type relationships between +> interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy +> difference is that when checking against a target intersection type, every constituent is checked +> before checking against the "effective"/"flattened" type. For this reason, extending types with +> interfaces/extends is suggested over creating intersection types. + +This is preferred: -const FancyButton = (props: FancyButtonProps) => { - ... +```ts +interface Foo extends Bar, Baz { + someProp: string; } ``` -or if you're using props destructuring: +Instead of this: +```ts +type Foo = Bar & + Baz & { + someProp: string; + }; ``` + +#### Why not use React.FC or FunctionComponent type? + +- Provides an implicit definition of `children` +- Doesn't support generics. +- Doesn't work correctly with `defaultProps` + +#### Implicit or explicit `returnType`? + +It depends. If we know we want to return a JSX element, it is not necessary to type the `returnType` +because it is inferred from what you actually return, so can be omitted most of the time. + +#### Preferred approach + +The best approach and example would be something like this: + +```ts interface FancyButtonProps { - color: string + color: string; } -const FancyButton = ({ color }: FancyButtonProps) => { - ... -} +export default function FancyButton({ color }: FancyButtonProps) => { + //... +}; +``` + +or if we want to explicity type the `returnType`: + +```ts +export default function FancyButton({ color }: FancyButtonProps): JSX.Element => { + //... +}; ``` ## GIT From 011725733acdfe0f5b8d8380884278043b59590b Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Sun, 18 Apr 2021 20:58:18 -0300 Subject: [PATCH 04/11] fix types and re-order information --- README.md | 41 ++++++++++++++--------------------------- 1 file changed, 14 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 08c3d6f..812af06 100644 --- a/README.md +++ b/README.md @@ -356,41 +356,26 @@ writable. Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible to modify the Array. -### React props & TypeScript - -#### Why use Interface instead of Type for typing props? +#### Preferring Interfaces Over Intersections `interface` are a little more powerful than the corresponding `type` declaration. One of the reasons to use `interface` is when you need to compose two or more types, you have the option of extending those types with an interface, or intersecting them in a type alias. TS team -says: - -> Interfaces create a single flat object type that detects property conflicts, which are usually -> important to resolve! Intersections on the other hand just recursively merge properties, and in -> some cases produce never. Interfaces also display consistently better, whereas type aliases to -> intersections can't be displayed in part of other intersections. Type relationships between -> interfaces are also cached, as opposed to intersection types as a whole. A final noteworthy -> difference is that when checking against a target intersection type, every constituent is checked -> before checking against the "effective"/"flattened" type. For this reason, extending types with -> interfaces/extends is suggested over creating intersection types. +[says](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections) -This is preferred: +Extending types with `interface`s/`extends` is suggested over creating intersection types. -```ts -interface Foo extends Bar, Baz { - someProp: string; -} +```diff +- type Foo = Bar & Baz & { +- someProp: string; +- } ++ interface Foo extends Bar, Baz { ++ someProp: string; ++ } ``` -Instead of this: - -```ts -type Foo = Bar & - Baz & { - someProp: string; - }; -``` +### TypeScript & React #### Why not use React.FC or FunctionComponent type? @@ -403,7 +388,9 @@ type Foo = Bar & It depends. If we know we want to return a JSX element, it is not necessary to type the `returnType` because it is inferred from what you actually return, so can be omitted most of the time. -#### Preferred approach +But if you might return something other than JSX, then define the `returnType`. + +#### Preferred approach for typing React props The best approach and example would be something like this: From b443bd1691cb8e00895550a160b5a35aa2b6f59f Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Sun, 18 Apr 2021 21:03:45 -0300 Subject: [PATCH 05/11] remove fat arrow typo on component function --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 812af06..c332c07 100644 --- a/README.md +++ b/README.md @@ -399,7 +399,7 @@ interface FancyButtonProps { color: string; } -export default function FancyButton({ color }: FancyButtonProps) => { +export default function FancyButton({ color }: FancyButtonProps) { //... }; ``` @@ -407,7 +407,7 @@ export default function FancyButton({ color }: FancyButtonProps) => { or if we want to explicity type the `returnType`: ```ts -export default function FancyButton({ color }: FancyButtonProps): JSX.Element => { +export default function FancyButton({ color }: FancyButtonProps): JSX.Element { //... }; ``` From 2fd3aec71757bf38c4335f16577a07ff8522a51e Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Sun, 18 Apr 2021 21:41:44 -0300 Subject: [PATCH 06/11] update typo for implicit or explicit returnType --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c332c07..040d22d 100644 --- a/README.md +++ b/README.md @@ -388,7 +388,7 @@ Extending types with `interface`s/`extends` is suggested over creating intersect It depends. If we know we want to return a JSX element, it is not necessary to type the `returnType` because it is inferred from what you actually return, so can be omitted most of the time. -But if you might return something other than JSX, then define the `returnType`. +But if you may going to return something other than JSX, then define the `returnType`. #### Preferred approach for typing React props From 11dfe4d5a951b1f534432c2ce19f07a661433570 Mon Sep 17 00:00:00 2001 From: Jacobo Martinez Date: Sun, 18 Apr 2021 21:52:33 -0300 Subject: [PATCH 07/11] update text context when refering to external link --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 040d22d..24ea5cf 100644 --- a/README.md +++ b/README.md @@ -361,8 +361,8 @@ Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible `interface` are a little more powerful than the corresponding `type` declaration. One of the reasons to use `interface` is when you need to compose two or more types, you have the -option of extending those types with an interface, or intersecting them in a type alias. TS team -[says](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections) +option of extending those types with an interface, or intersecting them in a type alias. For more information see what TS team said +[here](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections) Extending types with `interface`s/`extends` is suggested over creating intersection types. From 604d6b7b5f3bd7b4eb1bc2e36684ad7d6c5dd6c3 Mon Sep 17 00:00:00 2001 From: "Thi.js" Date: Fri, 12 Nov 2021 10:50:17 -0300 Subject: [PATCH 08/11] Update README.md Use `React.ReactElement` instead of `JSX.Element` Co-authored-by: Leroy Korterink <5376407+leroykorterink@users.noreply.github.com> --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 24ea5cf..45ef4a6 100644 --- a/README.md +++ b/README.md @@ -407,7 +407,7 @@ export default function FancyButton({ color }: FancyButtonProps) { or if we want to explicity type the `returnType`: ```ts -export default function FancyButton({ color }: FancyButtonProps): JSX.Element { +export default function FancyButton({ color }: FancyButtonProps): React.ReactElement { //... }; ``` From 09b56ed8cf040b90fb755574b68c367a0a4f93cf Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 18 Jan 2022 12:32:37 +0200 Subject: [PATCH 09/11] docs: update based on PR feedback --- README.md | 58 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 45ef4a6..4a5ac2b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ 4. [Formatting](#formatting) 5. [Comments](#comments) 6. [TypeScript](#typescript) - 1. [React props & TypeScript](#react-props-typescript) + 1. [React](#typescript-react) 7. [GIT](#git) 1. [Branches](#branches) 2. [Commit messages](#commit-messages) @@ -375,41 +375,53 @@ Extending types with `interface`s/`extends` is suggested over creating intersect + } ``` -### TypeScript & React - -#### Why not use React.FC or FunctionComponent type? - -- Provides an implicit definition of `children` -- Doesn't support generics. -- Doesn't work correctly with `defaultProps` +### React -#### Implicit or explicit `returnType`? +#### Avoid using `React.FC` and `FunctionComponent` built-ins of `@types/react` -It depends. If we know we want to return a JSX element, it is not necessary to type the `returnType` -because it is inferred from what you actually return, so can be omitted most of the time. +They provide an implicit definition of `children`, which can be misleading to components which do +not accept `children`. It is encouraged to instead either define `children?: ReactNode` on your prop +type or wrap your prop type in the `PropsWithChildren` type util. -But if you may going to return something other than JSX, then define the `returnType`. +```ts +type MonkComponentProps = { + // other properties + children?: ReactNode; +}; -#### Preferred approach for typing React props +function MonkComponent({ children }: MonkComponentProps): ReactElement { + //... +} -The best approach and example would be something like this: +// with `PropsWithChildren` -```ts -interface FancyButtonProps { - color: string; -} +type MonkComponentProps = { + // other properties +}; -export default function FancyButton({ color }: FancyButtonProps) { +function MonkComponent({ children }: PropsWithChildren): ReactElement { //... -}; +} ``` -or if we want to explicity type the `returnType`: +As they are built-ins, they do not allow for custom generics and thus do not support +[Generic Components](https://react-typescript-cheatsheet.netlify.app/docs/advanced/patterns_by_usecase/#generic-components). + +#### Always define a return type + +In addition to its own components, React allows returning +[primitive](https://developer.mozilla.org/en-US/docs/Glossary/Primitive) values from components. +While this is nice, it sometimes might be a mistake to return a primitive, instead of a valid +component; this can most commonly happen when your component renders itself conditionally (e.g. +using logical operators). + +TypeScript return type inference would inherently "hide" this potential bug from us, because of this +we strongly encourage to type the return types of components: ```ts -export default function FancyButton({ color }: FancyButtonProps): React.ReactElement { +function MonkComponent(): ReactElement { //... -}; +} ``` ## GIT From 5920fc480b2cd2ca5242f6ee34a74316e5a56596 Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 18 Jan 2022 12:33:48 +0200 Subject: [PATCH 10/11] docs: remove interface preference section as not relevant to PR --- README.md | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/README.md b/README.md index 4a5ac2b..6440f30 100644 --- a/README.md +++ b/README.md @@ -356,25 +356,6 @@ writable. Always prefer `ReadonlyArray` over a regular `Array` unless it must be possible to modify the Array. -#### Preferring Interfaces Over Intersections - -`interface` are a little more powerful than the corresponding `type` declaration. - -One of the reasons to use `interface` is when you need to compose two or more types, you have the -option of extending those types with an interface, or intersecting them in a type alias. For more information see what TS team said -[here](https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections) - -Extending types with `interface`s/`extends` is suggested over creating intersection types. - -```diff -- type Foo = Bar & Baz & { -- someProp: string; -- } -+ interface Foo extends Bar, Baz { -+ someProp: string; -+ } -``` - ### React #### Avoid using `React.FC` and `FunctionComponent` built-ins of `@types/react` From 79cdbf898bc37ef68629f02c37625ac65eea626c Mon Sep 17 00:00:00 2001 From: Paul Date: Tue, 18 Jan 2022 12:41:18 +0200 Subject: [PATCH 11/11] docs: add a bit on the React/TypeScript cheatsheet --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6440f30..1847c9e 100644 --- a/README.md +++ b/README.md @@ -405,6 +405,8 @@ function MonkComponent(): ReactElement { } ``` +> We recommend checking out https://react-typescript-cheatsheet.netlify.app for more in-depth and advanced usages of TypeScript within React + ## GIT ### Branches