Skip to content

Commit 9985de7

Browse files
Replace invalid props example (#619)
1 parent 7d96263 commit 9985de7

File tree

3 files changed

+92
-24
lines changed

3 files changed

+92
-24
lines changed

README.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,18 +1177,32 @@ type AppProps = {
11771177
};
11781178
```
11791179

1180-
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](https://react-typescript-cheatsheet.netlify.app/docs/advanced/misc_concerns/#commenting-components) section in the Advanced Cheatsheet.
1180+
##### `object` as the non-primitive type
11811181

1182-
<details>
1183-
<summary>More on object types: <code>object</code>, <code>{"{}"}</code>, etc</summary>
1182+
`object` is a common source of misunderstanding in TypeScript. It does not mean "any object" but rather "any non-primitive type", which means it represents anything that is not `number`, `string`, `boolean`, `symbol`, `null` or `undefined`.
11841183

1185-
In Typescript, it's generally best to use specific types for objects. In most cases, this means a literal type like <code>{ id: string; name: string }</code>. In cases where there isn't a fixed structure for an object, you likely either want an index signature (possibly with the <code>Record</code> shorthand) - if there are values of a certain type, but the keys can change - or else <a href="https://www.typescriptlang.org/docs/handbook/2/generics.html">generics</a> - if the object structure is more-or-less an arbitrary black-box.
1184+
Typing "any non-primitive value" is most likely not something that you should do much in React, which means you will probably not use `object` much.
11861185

1187-
Another approach to objects is the <code>Map</code> data structure, but this is somewhat uncommon to use in React, because React prefers data to be changed immutably (e.g. <code>setUser({...user, name: newName})</code>), while Maps are mutable data structures.
1186+
##### Empty interface, `{}` and `Object`
11881187

1189-
"Vague" object types like <code>object</code>, <code>{}</code> are fairly niche and should be rarely used, and may function differently than you expect. <code>object</code> is any non-primitive value: this includes things like functions and arrays and constructors, not just "simple" objects. And <code>{}</code> is perhaps better thought of as "an interface with no required properties", not "an empty object" - in practice this type allows anything except <code>null</code> or <code>undefined</code>. <code>Object</code> behaves the same as <code>{}</code> and is basically never used.
1188+
An empty interface, `{}` and `Object` all represent "any non-nullish value"—not "an empty object" as you might think. [Using these types is a common source of misunderstanding and is not recommended](https://typescript-eslint.io/rules/no-empty-interface/).
11901189

1191-
</details>
1190+
```ts
1191+
interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object`
1192+
1193+
let value: AnyNonNullishValue;
1194+
1195+
// these are all fine, but might not be expected
1196+
value = 1;
1197+
value = "foo";
1198+
value = () => alert("foo");
1199+
value = {};
1200+
value = { foo: "bar" };
1201+
1202+
// these are errors
1203+
value = undefined;
1204+
value = null;
1205+
```
11921206

11931207
#### Useful React Prop Type Examples
11941208

docs/advanced/patterns_by_usecase.md

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -709,23 +709,63 @@ const UsageComponent = () => (
709709

710710
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA)
711711

712-
Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable)
712+
## Props: Pass nothing or all
713713

714-
## Props: Must Pass Both
714+
Passing no props is equivalent to passing an empty object. However, the type for an empty object is not `{}`, which you might think. [Make sure you understand what empty interface, `{}` and `Object` means](/docs/basic/getting-started/basic_type_example#empty-interface--and-object). `Record<string, never>` is probably the closest you can get to an empty object type, and is [recommended by typescript-eslint](https://typescript-eslint.io/rules/ban-types/). Here's an example of allowing "nothing or all":
715715

716716
```tsx
717-
type OneOrAnother<T1, T2> =
718-
| (T1 & { [K in keyof T2]?: undefined })
719-
| (T2 & { [K in keyof T1]?: undefined });
717+
type Nothing = Record<string, never>;
720718

721-
type Props = OneOrAnother<{ a: string; b: string }, {}>;
719+
interface All {
720+
a: string;
721+
b: string;
722+
}
723+
724+
const NothingOrAll = (props: Nothing | All) => {
725+
if ("a" in props) {
726+
return <>{props.b}</>;
727+
}
728+
return <>Nothing</>;
729+
};
722730

723-
const a: Props = { a: "a" }; // error
724-
const b: Props = { b: "b" }; // error
725-
const ab: Props = { a: "a", b: "b" }; // ok
731+
const Component = () => (
732+
<>
733+
<NothingOrAll /> {/* ok */}
734+
<NothingOrAll a="" /> {/* error */}
735+
<NothingOrAll b="" /> {/* error */}
736+
<NothingOrAll a="" b="" /> {/* ok */}
737+
</>
738+
);
739+
```
740+
741+
While this works, representing and empty object with `Record<string, never>` [is not officially recommended](https://github.com/microsoft/TypeScript/issues/47486#issuecomment-1015671856). It might be better approaching this in another way, to avoid trying to type "an exactly empty object". One way is grouping the required props in an optional object:
742+
743+
```tsx
744+
interface Props {
745+
obj?: {
746+
a: string;
747+
b: string;
748+
};
749+
}
750+
751+
const NothingOrAll = (props: Props) => {
752+
if (props.obj) {
753+
return <>{props.obj.a}</>;
754+
}
755+
return <>Nothing</>;
756+
};
757+
758+
const Component = () => (
759+
<>
760+
<NothingOrAll /> {/* ok */}
761+
<NothingOrAll obj={{ a: "" }} /> {/* error */}
762+
<NothingOrAll obj={{ b: "" }} /> {/* error */}
763+
<NothingOrAll obj={{ a: "", b: "" }} /> {/* ok */}
764+
</>
765+
);
726766
```
727767

728-
Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
768+
Another way is to make both props optional and then check that either none or all props are passed at runtime.
729769

730770
## Props: Pass One ONLY IF the Other Is Passed
731771

docs/basic/getting-started/basic-type-examples.md

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,32 @@ type AppProps = {
5454
};
5555
```
5656

57-
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](https://react-typescript-cheatsheet.netlify.app/docs/advanced/misc_concerns/#commenting-components) section in the Advanced Cheatsheet.
57+
### `object` as the non-primitive type
5858

59-
<details>
60-
<summary>More on object types: <code>object</code>, <code>{"{}"}</code>, etc</summary>
59+
`object` is a common source of misunderstanding in TypeScript. It does not mean "any object" but rather "any non-primitive type", which means it represents anything that is not `number`, `string`, `boolean`, `symbol`, `null` or `undefined`.
6160

62-
In Typescript, it's generally best to use specific types for objects. In most cases, this means a literal type like <code>{ id: string; name: string }</code>. In cases where there isn't a fixed structure for an object, you likely either want an index signature (possibly with the <code>Record</code> shorthand) - if there are values of a certain type, but the keys can change - or else <a href="https://www.typescriptlang.org/docs/handbook/2/generics.html">generics</a> - if the object structure is more-or-less an arbitrary black-box.
61+
Typing "any non-primitive value" is most likely not something that you should do much in React, which means you will probably not use `object` much.
6362

64-
Another approach to objects is the <code>Map</code> data structure, but this is somewhat uncommon to use in React, because React prefers data to be changed immutably (e.g. <code>setUser({...user, name: newName})</code>), while Maps are mutable data structures.
63+
### Empty interface, `{}` and `Object`
6564

66-
"Vague" object types like <code>object</code>, <code>{}</code> are fairly niche and should be rarely used, and may function differently than you expect. <code>object</code> is any non-primitive value: this includes things like functions and arrays and constructors, not just "simple" objects. And <code>{}</code> is perhaps better thought of as "an interface with no required properties", not "an empty object" - in practice this type allows anything except <code>null</code> or <code>undefined</code>. <code>Object</code> behaves the same as <code>{}</code> and is basically never used.
65+
An empty interface, `{}` and `Object` all represent "any non-nullish value"—not "an empty object" as you might think. [Using these types is a common source of misunderstanding and is not recommended](https://typescript-eslint.io/rules/no-empty-interface/).
6766

68-
</details>
67+
```ts
68+
interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object`
69+
70+
let value: AnyNonNullishValue;
71+
72+
// these are all fine, but might not be expected
73+
value = 1;
74+
value = "foo";
75+
value = () => alert("foo");
76+
value = {};
77+
value = { foo: "bar" };
78+
79+
// these are errors
80+
value = undefined;
81+
value = null;
82+
```
6983

7084
## Useful React Prop Type Examples
7185

0 commit comments

Comments
 (0)