diff --git a/README.md b/README.md
index 0171486f..a21bb38e 100644
--- a/README.md
+++ b/README.md
@@ -1177,18 +1177,32 @@ type AppProps = {
};
```
-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.
+##### `object` as the non-primitive type
-
-More on object types: object
, {"{}"}
, etc
+`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`.
-In Typescript, it's generally best to use specific types for objects. In most cases, this means a literal type like { id: string; name: string }
. In cases where there isn't a fixed structure for an object, you likely either want an index signature (possibly with the Record
shorthand) - if there are values of a certain type, but the keys can change - or else generics - if the object structure is more-or-less an arbitrary black-box.
+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.
-Another approach to objects is the Map
data structure, but this is somewhat uncommon to use in React, because React prefers data to be changed immutably (e.g. setUser({...user, name: newName})
), while Maps are mutable data structures.
+##### Empty interface, `{}` and `Object`
-"Vague" object types like object
, {}
are fairly niche and should be rarely used, and may function differently than you expect. object
is any non-primitive value: this includes things like functions and arrays and constructors, not just "simple" objects. And {}
is perhaps better thought of as "an interface with no required properties", not "an empty object" - in practice this type allows anything except null
or undefined
. Object
behaves the same as {}
and is basically never used.
+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/).
-
+```ts
+interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object`
+
+let value: AnyNonNullishValue;
+
+// these are all fine, but might not be expected
+value = 1;
+value = "foo";
+value = () => alert("foo");
+value = {};
+value = { foo: "bar" };
+
+// these are errors
+value = undefined;
+value = null;
+```
#### Useful React Prop Type Examples
diff --git a/docs/advanced/patterns_by_usecase.md b/docs/advanced/patterns_by_usecase.md
index 88f21f07..0a906e35 100644
--- a/docs/advanced/patterns_by_usecase.md
+++ b/docs/advanced/patterns_by_usecase.md
@@ -709,23 +709,63 @@ const UsageComponent = () => (
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA)
-Further reading: [how to ban passing `{}` if you have a `NoFields` type.](http://www.javiercasas.com/articles/typescript-impossible-states-irrepresentable)
+## Props: Pass nothing or all
-## Props: Must Pass Both
+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` 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":
```tsx
-type OneOrAnother =
- | (T1 & { [K in keyof T2]?: undefined })
- | (T2 & { [K in keyof T1]?: undefined });
+type Nothing = Record;
-type Props = OneOrAnother<{ a: string; b: string }, {}>;
+interface All {
+ a: string;
+ b: string;
+}
+
+const NothingOrAll = (props: Nothing | All) => {
+ if ("a" in props) {
+ return <>{props.b}>;
+ }
+ return <>Nothing>;
+};
-const a: Props = { a: "a" }; // error
-const b: Props = { b: "b" }; // error
-const ab: Props = { a: "a", b: "b" }; // ok
+const Component = () => (
+ <>
+ {/* ok */}
+ {/* error */}
+ {/* error */}
+ {/* ok */}
+ >
+);
+```
+
+While this works, representing and empty object with `Record` [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:
+
+```tsx
+interface Props {
+ obj?: {
+ a: string;
+ b: string;
+ };
+}
+
+const NothingOrAll = (props: Props) => {
+ if (props.obj) {
+ return <>{props.obj.a}>;
+ }
+ return <>Nothing>;
+};
+
+const Component = () => (
+ <>
+ {/* ok */}
+ {/* error */}
+ {/* error */}
+ {/* ok */}
+ >
+);
```
-Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
+Another way is to make both props optional and then check that either none or all props are passed at runtime.
## Props: Pass One ONLY IF the Other Is Passed
diff --git a/docs/basic/getting-started/basic-type-examples.md b/docs/basic/getting-started/basic-type-examples.md
index d8a12ba0..043a5a22 100644
--- a/docs/basic/getting-started/basic-type-examples.md
+++ b/docs/basic/getting-started/basic-type-examples.md
@@ -54,18 +54,32 @@ type AppProps = {
};
```
-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.
+### `object` as the non-primitive type
-
-More on object types: object
, {"{}"}
, etc
+`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`.
-In Typescript, it's generally best to use specific types for objects. In most cases, this means a literal type like { id: string; name: string }
. In cases where there isn't a fixed structure for an object, you likely either want an index signature (possibly with the Record
shorthand) - if there are values of a certain type, but the keys can change - or else generics - if the object structure is more-or-less an arbitrary black-box.
+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.
-Another approach to objects is the Map
data structure, but this is somewhat uncommon to use in React, because React prefers data to be changed immutably (e.g. setUser({...user, name: newName})
), while Maps are mutable data structures.
+### Empty interface, `{}` and `Object`
-"Vague" object types like object
, {}
are fairly niche and should be rarely used, and may function differently than you expect. object
is any non-primitive value: this includes things like functions and arrays and constructors, not just "simple" objects. And {}
is perhaps better thought of as "an interface with no required properties", not "an empty object" - in practice this type allows anything except null
or undefined
. Object
behaves the same as {}
and is basically never used.
+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/).
-
+```ts
+interface AnyNonNullishValue {} // equivalent to `type AnyNonNullishValue = {}` or `type AnyNonNullishValue = Object`
+
+let value: AnyNonNullishValue;
+
+// these are all fine, but might not be expected
+value = 1;
+value = "foo";
+value = () => alert("foo");
+value = {};
+value = { foo: "bar" };
+
+// these are errors
+value = undefined;
+value = null;
+```
## Useful React Prop Type Examples