Skip to content

Commit 90442d2

Browse files
Fix one or the other prop pattern example (#620)
1 parent e12f810 commit 90442d2

File tree

1 file changed

+49
-19
lines changed

1 file changed

+49
-19
lines changed

docs/advanced/patterns_by_usecase.md

+49-19
Original file line numberDiff line numberDiff line change
@@ -681,33 +681,63 @@ function isString(a: unknown): a is string {
681681

682682
See this quick guide: https://twitter.com/mpocock1/status/1500813765973053440?s=20&t=ImUA-NnZc4iUuPDx-XiMTA
683683

684-
## Props: One or the Other but not Both
684+
## Props: One or the other but not both
685685

686-
Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both:
686+
Since TypeScript is a structural type system, _minimum_ property requirements are described rather than _exact_, which makes it a bit more difficult to disallow certain props compared to what you might think. `never` can be used to allow either one or another prop, but not both:
687687

688688
```tsx
689-
type Props1 = { foo: string };
690-
type Props2 = { bar: string };
689+
type Props1 = { foo: string; bar?: never };
690+
type Props2 = { bar: string; foo?: never };
691691

692-
function MyComponent(props: Props1 | Props2) {
693-
if ("foo" in props) {
694-
// props.bar // error
695-
return <div>{props.foo}</div>;
696-
} else {
697-
// props.foo // error
698-
return <div>{props.bar}</div>;
692+
const OneOrTheOther = (props: Props1 | Props2) => {
693+
if ("foo" in props && typeof props.foo === "string") {
694+
// `props.bar` is of type `undefined`
695+
return <>{props.foo}</>;
699696
}
700-
}
701-
const UsageComponent = () => (
702-
<div>
703-
<MyComponent foo="foo" />
704-
<MyComponent bar="bar" />
705-
{/* <MyComponent foo="foo" bar="bar"/> // invalid */}
706-
</div>
697+
// `props.foo` is of type `undefined`
698+
return <>{props.bar}</>;
699+
};
700+
const Component = () => (
701+
<>
702+
<OneOrTheOther /> {/* error */}
703+
<OneOrTheOther foo="" /> {/* ok */}
704+
<OneOrTheOther bar="" /> {/* ok */}
705+
<OneOrTheOther foo="" bar="" /> {/* error */}
706+
</>
707+
);
708+
```
709+
710+
[View in the TypeScript Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSYBPMJOABRzAGcBGOAXjgG85MIIAXHCYwowAHYBzANxwARiigB+IeKQA3JFDgBfaRWq0GEZgCYO3eYqEixU2Xwgq4azdr3k0EcSLgB5NT8oABUACyQ-GHDtTgAKMEYmIWNmNgAfekTTAEoOAD5uUjg4YEw4WPxHfBLxOASTJjgAMia4KhoIMvrmADpHDnZOfFsJSXxcriLiuAB6GbgAA26mHoUoBZLGzrbDRYBXcQATJEwJJEOFqeKiGD2oWoAePK5lvv4dB5m8-WKdKbnFq9HBtgFsyu1aAsDsdTmoLlMbndHs9XmsPl99B4vD54ABhXCQNTieBxXLsAqxKZPK5wB4BCIhcKRaKzApcGYAKjgWhw2g5Mz+01p9KCYQiUS0vH47Hw1S+3E5cAgAGs4PzBdM6YFGeKWWsZXK2YqVWqBTStQyxczJY4DVYoHb5eyuTzoKaNZ88qRstIgA).
711+
712+
A better alternative might be to use a discriminant prop like this:
713+
714+
```tsx
715+
type Props1 = { type: "foo"; foo: string };
716+
type Props2 = { type: "bar"; bar: string };
717+
718+
const OneOrTheOther = (props: Props1 | Props2) => {
719+
if (props.type === "foo") {
720+
// `props.bar` does not exist
721+
return <>{props.foo}</>;
722+
}
723+
// `props.foo` does not exist
724+
return <>{props.bar}</>;
725+
};
726+
const Component = () => (
727+
<>
728+
<OneOrTheOther type="foo" /> {/* error */}
729+
<OneOrTheOther type="foo" foo="" /> {/* ok */}
730+
<OneOrTheOther type="foo" bar="" /> {/* error */}
731+
<OneOrTheOther type="foo" foo="" bar="" /> {/* error */}
732+
<OneOrTheOther type="bar" /> {/* error */}
733+
<OneOrTheOther type="bar" foo="" /> {/* error */}
734+
<OneOrTheOther type="bar" bar="" /> {/* ok */}
735+
<OneOrTheOther type="bar" foo="" bar="" /> {/* error */}
736+
</>
707737
);
708738
```
709739

710-
[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA)
740+
[View in the TypeScript Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSYBPMJOABRzAGcBGOAXjgG84qaAuApggR8AbjjCIgpjCjAAdgHM4AXzEVqtBhGYAmDt15bB+AEYoo4uBagy5ilevJoIC2XADyCpJ6gAVAAtfGGCoQwAKMEYmQR1mNgAfehi9AEoOAD5uUjg4YEw4KJiAOj5adkqhEXwMrly8uAB6JrgAA2jdJhLbNrgAEwgkJjgFCHgkAA9gWQa8ohgAVygFOAAeTK5O5hKpVTWmzI081QaW9u3uqT7B4dHxuCmZmAaF5dWNrdLbfcONZ1c7ngAGFcJAfAp4JwIhl2NkIg0NnN1t5fAFgp5QkhwuV2PgpPhmtkuE0AFSPKA4cKkpqnRoonx+IIhMLGGh4gmSER4wmHbhkuAQADWcBpdMaa1RTIxWJxWg5NRslh5RP55OxVNFtORksZ6JZ2LZSAVoi5EBVthVfJJ6sp0C14ryurRzMxrNx5ksvOJAo19rFOql+rdho9tkJUitPttmoD9Od0oNcvZnqsSqgUbVgpFcYlQddsqNePDZotyvw3qzfup2qdh1IaTEQA).
711741

712742
## Props: Pass nothing or all
713743

0 commit comments

Comments
 (0)