Skip to content

Commit 1ac2dec

Browse files
authored
add prop one or the other or both hints
1 parent 1cf6462 commit 1ac2dec

File tree

1 file changed

+47
-0
lines changed

1 file changed

+47
-0
lines changed

ADVANCED.md

+47
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
* [Higher Order Components](#higher-order-components-hocs)
1818
* [Render Props](#render-props)
1919
* [Types for Conditional Rendering](#types-for-conditional-rendering)
20+
* [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both)
21+
* [Props: Must Pass Both](#props-one-or-the-other-but-not-both)
2022
* [Omit attribute from a type](#omit-attribute-from-a-type)
2123
* [Type Zoo](#type-zoo)
2224
* [Extracting Prop Types of a Component](#extracting-prop-types-of-a-component)
@@ -221,6 +223,51 @@ const LinkButton: React.FunctionComponent<RouterLinkProps> = (props) => (
221223
<AnchorButton href="/login" to="/test">Login</AnchorButton> // Error: Property 'to' does not exist on type...
222224
```
223225

226+
## Props: One or the Other but not Both
227+
228+
Use the `in` keyword, function overloading, and union types to make components that take either one or another sets of props, but not both:
229+
230+
```tsx
231+
type Props1 = { foo: string }
232+
type Props2 = { bar: string }
233+
234+
function MyComponent(props: Props1): JSX.Element
235+
function MyComponent(props: Props2): JSX.Element
236+
function MyComponent(props: Props1 | Props2) {
237+
if ('foo' in props) {
238+
// props.bar // error
239+
return <div>{props.foo}</div>
240+
} else {
241+
// props.foo // error
242+
return <div>{props.bar}</div>
243+
}
244+
}
245+
const UsageComponent: React.FC = () => (
246+
<div>
247+
<MyComponent foo="foo" />
248+
<MyComponent bar="bar" />
249+
{/* <MyComponent foo="foo" bar="bar"/> // invalid */}
250+
</div>
251+
)
252+
```
253+
254+
255+
## Props: Must Pass Both
256+
257+
```tsx
258+
type OneOrAnother<T1, T2> =
259+
| (T1 & { [K in keyof T2]?: undefined })
260+
| (T2 & { [K in keyof T1]?: undefined })
261+
262+
type Props = OneOrAnother<{ a: string; b: string }, {}>
263+
264+
const a: Props = { a: 'a' } // error
265+
const b: Props = { b: 'b' } // error
266+
const ab: Props = { a: 'a', b: 'b' } // ok
267+
```
268+
269+
Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
270+
224271
## Omit attribute from a type
225272

226273
Sometimes when intersecting types, we want to define our own version of an attribute. For example, I want my component to have a `label`, but the type I am intersecting with also has a `label` attribute. Here's how to extract that out:

0 commit comments

Comments
 (0)