Skip to content

Commit f52f662

Browse files
authored
add commentary about readonly
1 parent 6b68bfb commit f52f662

File tree

1 file changed

+43
-12
lines changed

1 file changed

+43
-12
lines changed

README.md

+43-12
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ import ReactDOM from 'react-dom';
100100

101101
<summary>Explanation</summary>
102102

103-
Why not `esModuleInterop`? [Daniel Rosenwasser](https://twitter.com/drosenwasser/status/1003097042653073408) has said that it's better for webpack/parcel. For more discussion check out <https://github.com/wmonk/create-react-app-typescript/issues/214>
103+
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 <https://github.com/wmonk/create-react-app-typescript/issues/214>
104104

105105
Please PR or [File an issue](https://github.com/sw-yx/react-typescript-cheatsheet/issues/new) with your suggestions!
106106
</details>
@@ -110,9 +110,7 @@ Please PR or [File an issue](https://github.com/sw-yx/react-typescript-cheatshee
110110

111111
## Function Components
112112

113-
*Contributed by: [@jasanst](https://github.com/sw-yx/react-typescript-cheatsheet/pull/9) and [@tpetrina](https://github.com/sw-yx/react-typescript-cheatsheet/pull/21)*
114-
115-
You can specify the type of props as you use them:
113+
You can specify the type of props as you use them and rely on type inference:
116114

117115
```tsx
118116
const App = ({ message }: { message: string }) => <div>{message}</div>;
@@ -121,15 +119,18 @@ const App = ({ message }: { message: string }) => <div>{message}</div>;
121119
Or you can use the provided generic type for function components:
122120

123121
```tsx
124-
// React.FunctionComponent also works
125-
const App: React.FC<{ message: string }> = ({ message }) => <div>{message}</div>;
122+
const App: React.FC<{ message: string }> = ({ message }) => <div>{message}</div>; // React.FunctionComponent also works
126123
```
127124

128125
<details>
129126

130127
<summary><b>What's the difference?</b></summary>
131128

132-
The former pattern is shorter, so why would people use `React.FunctionComponent` at all? If you need to use `children` property inside the function body, in the former case it has to be added explicitly. `FunctionComponent<T>` 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`.
129+
The former pattern is shorter, so why would people use `React.FunctionComponent` at all?
130+
131+
- If you need to use `children` property inside the function body, in the former case it has to be added explicitly. `FunctionComponent<T>` already includes the correctly typed `children` property which then doesn't have to become part of your type.
132+
- Typing your function explicitly will also give you typechecking and autocomplete on its static properties, like `displayName`, `propTypes`, and `defaultProps`.
133+
- *In future*, it will also set `readonly` on your props just like `React.Component<T>` does.
133134

134135
```tsx
135136
const Title: React.FunctionComponent<{ title: string }> = ({ children, title }) => (
@@ -148,19 +149,27 @@ const App: React.FunctionComponent<{ message: string }> = function App({ message
148149
</details>
149150

150151
<details>
151-
<summary><b>Common Pitfalls</b></summary>
152+
<summary><b>Minor Pitfalls</b></summary>
152153

153154
These patterns are not supported:
154155

156+
**Conditional rendering**
157+
155158
```tsx
156-
const MyConditionalComponent = ({ shouldRender = false }) => shouldRender ? <div /> : false
159+
const MyConditionalComponent = ({ shouldRender = false }) => shouldRender ? <div /> : false // don't do this in JS either
157160
const el = <MyConditionalComponent /> // throws an error
161+
```
162+
163+
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`.
158164

165+
```tsx
159166
const MyArrayComponent = () => Array(5).fill(<div />)
160-
const el2 = <MyArrayComponentt /> // throws an error
167+
const el2 = <MyArrayComponent /> // throws an error
161168
```
162169

163-
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`. 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:
170+
**Array.fill**
171+
172+
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:
164173

165174
```tsx
166175
const MyArrayComponent = () => Array(5).fill(<div />) as any as JSX.Element
@@ -251,13 +260,35 @@ Don't forget that you can export/import/extend these types/interfaces for reuse.
251260
<details>
252261
<summary><b>Why annotate `state` twice?</b></summary>
253262

254-
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.
263+
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.
264+
265+
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.
255266

256267
[See commentary by @ferdaber here](https://github.com/sw-yx/react-typescript-cheatsheet/issues/57).
257268

258269
</details>
259270

260271

272+
<details>
273+
<summary><b>No need for <code>readonly</code></b></summary>
274+
275+
You often see sample code include `readonly` to mark props and state immutable:
276+
277+
```tsx
278+
type MyProps = {
279+
readonly message: string
280+
}
281+
type MyState = {
282+
readonly count: number
283+
}
284+
```
285+
286+
This is not necessary as `React.Component<P,S>` already marks them as immutable. ([See PR and discussion!](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/26813))
287+
288+
</details>
289+
290+
291+
261292
**Class Methods**: Do it like normal, but just remember any arguments for your functions also need to be typed:
262293
263294
```tsx

0 commit comments

Comments
 (0)