From ee815d96ef6bbea394e9cb684ae9e1bfe8a2f9cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Tammerg=C3=A5rd?= Date: Thu, 6 Apr 2023 09:13:41 +0200 Subject: [PATCH] Fix one or the other prop pattern example --- docs/advanced/patterns_by_usecase.md | 68 ++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 19 deletions(-) diff --git a/docs/advanced/patterns_by_usecase.md b/docs/advanced/patterns_by_usecase.md index 0a906e35..7ff00f6b 100644 --- a/docs/advanced/patterns_by_usecase.md +++ b/docs/advanced/patterns_by_usecase.md @@ -681,33 +681,63 @@ function isString(a: unknown): a is string { See this quick guide: https://twitter.com/mpocock1/status/1500813765973053440?s=20&t=ImUA-NnZc4iUuPDx-XiMTA -## Props: One or the Other but not Both +## Props: One or the other but not both -Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both: +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: ```tsx -type Props1 = { foo: string }; -type Props2 = { bar: string }; +type Props1 = { foo: string; bar?: never }; +type Props2 = { bar: string; foo?: never }; -function MyComponent(props: Props1 | Props2) { - if ("foo" in props) { - // props.bar // error - return
{props.foo}
; - } else { - // props.foo // error - return
{props.bar}
; +const OneOrTheOther = (props: Props1 | Props2) => { + if ("foo" in props && typeof props.foo === "string") { + // `props.bar` is of type `undefined` + return <>{props.foo}; } -} -const UsageComponent = () => ( -
- - - {/* // invalid */} -
+ // `props.foo` is of type `undefined` + return <>{props.bar}; +}; +const Component = () => ( + <> + {/* error */} + {/* ok */} + {/* ok */} + {/* error */} + +); +``` + +[View in the TypeScript Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSYBPMJOABRzAGcBGOAXjgG85MIIAXHCYwowAHYBzANxwARiigB+IeKQA3JFDgBfaRWq0GEZgCYO3eYqEixU2Xwgq4azdr3k0EcSLgB5NT8oABUACyQ-GHDtTgAKMEYmIWNmNgAfekTTAEoOAD5uUjg4YEw4WPxHfBLxOASTJjgAMia4KhoIMvrmADpHDnZOfFsJSXxcriLiuAB6GbgAA26mHoUoBZLGzrbDRYBXcQATJEwJJEOFqeKiGD2oWoAePK5lvv4dB5m8-WKdKbnFq9HBtgFsyu1aAsDsdTmoLlMbndHs9XmsPl99B4vD54ABhXCQNTieBxXLsAqxKZPK5wB4BCIhcKRaKzApcGYAKjgWhw2g5Mz+01p9KCYQiUS0vH47Hw1S+3E5cAgAGs4PzBdM6YFGeKWWsZXK2YqVWqBTStQyxczJY4DVYoHb5eyuTzoKaNZ88qRstIgA). + +A better alternative might be to use a discriminant prop like this: + +```tsx +type Props1 = { type: "foo"; foo: string }; +type Props2 = { type: "bar"; bar: string }; + +const OneOrTheOther = (props: Props1 | Props2) => { + if (props.type === "foo") { + // `props.bar` does not exist + return <>{props.foo}; + } + // `props.foo` does not exist + return <>{props.bar}; +}; +const Component = () => ( + <> + {/* error */} + {/* ok */} + {/* error */} + {/* error */} + {/* error */} + {/* error */} + {/* ok */} + {/* error */} + ); ``` -[View in the TypeScript Playground](https://www.typescriptlang.org/play/?jsx=2#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgcilQ3wFgAoCmATzCTgAUcwBnARjgF44BvOTCBABccFjCjAAdgHM4AXwDcVWvSYRWAJi684AIxRQRYiTPlLK5TAFdJGYBElwAstQDCuSJKSSYACjDMLCJqrBwAPoyBGgCUvBRwcMCYcL4ARAIQqYmOAeossTzxCXAA9CVwuawAdPpQpeVIUDhQRQlEMFZQjgA8ACbAAG4AfDyVLFUZct0l-cPmCXJwSAA2LPSF5MX1FYETgtuNza1w7Z09syNjNQZTM4ND8-IUchRoDmJwAKosKNJI7uAHN4YCJkOgYFUAGKubS+WKcIYpIp9e7HbouAGeYH8QScdKCLIlIZojEeIE+PQGPG1QnEzbFHglABUcHRbjJXgpGTxGSytWpBlSRO2UgGKGWwF6cCZJRe9OmFwo0QUQA) +[View in the TypeScript Playground](https://www.typescriptlang.org/play#code/JYWwDg9gTgLgBAJQKYEMDG8BmUIjgIilQ3wChSYBPMJOABRzAGcBGOAXjgG84qaAuApggR8AbjjCIgpjCjAAdgHM4AXzEVqtBhGYAmDt15bB+AEYoo4uBagy5ilevJoIC2XADyCpJ6gAVAAtfGGCoQwAKMEYmQR1mNgAfehi9AEoOAD5uUjg4YEw4KJiAOj5adkqhEXwMrly8uAB6JrgAA2jdJhLbNrgAEwgkJjgFCHgkAA9gWQa8ohgAVygFOAAeTK5O5hKpVTWmzI081QaW9u3uqT7B4dHxuCmZmAaF5dWNrdLbfcONZ1c7ngAGFcJAfAp4JwIhl2NkIg0NnN1t5fAFgp5QkhwuV2PgpPhmtkuE0AFSPKA4cKkpqnRoonx+IIhMLGGh4gmSER4wmHbhkuAQADWcBpdMaa1RTIxWJxWg5NRslh5RP55OxVNFtORksZ6JZ2LZSAVoi5EBVthVfJJ6sp0C14ryurRzMxrNx5ksvOJAo19rFOql+rdho9tkJUitPttmoD9Od0oNcvZnqsSqgUbVgpFcYlQddsqNePDZotyvw3qzfup2qdh1IaTEQA). ## Props: Pass nothing or all