diff --git a/ADVANCED.md b/ADVANCED.md
index 717c13a7..d5f0b118 100644
--- a/ADVANCED.md
+++ b/ADVANCED.md
@@ -47,7 +47,8 @@
- [`as` props (passing a component to be rendered)](#as-props-passing-a-component-to-be-rendered)
- [Types for Conditional Rendering](#types-for-conditional-rendering)
- [Props: One or the Other but not Both](#props-one-or-the-other-but-not-both)
- - [Props: Must Pass Both](#props-one-or-the-other-but-not-both)
+ - [Props: Must Pass Both](#props-must-pass-both)
+ - [Props: Can Optionally Pass One Only If the Other Is Passed](#props-can-optionally-pass-one-only-if-the-other-is-passed)
- [Omit attribute from a type](#omit-attribute-from-a-type)
- [Type Zoo](#type-zoo)
- [Extracting Prop Types of a Component](#extracting-prop-types-of-a-component)
@@ -451,6 +452,81 @@ const ab: Props = { a: 'a', b: 'b' }; // ok
Thanks [diegohaz](https://twitter.com/kentcdodds/status/1085655423611367426)
+## Props: Can Optionally Pass One Only If the Other Is Passed
+
+Say you want a Text component that gets truncated if `truncate` prop is passed but expands to show the full text when `expanded` prop is passed (e.g. when the user clicks the text).
+
+You want to allow `expanded` to be passed only if `truncate` is also passed, because there is no use for `expanded` if the text is not truncated.
+
+You can do this by function overloads:
+
+```tsx
+import React from "react";
+
+type CommonProps = {
+ children: React.ReactNode;
+ as: "p" | "span" | "h1" | "h2" | "h3" | "h4" | "h5" | "h6";
+};
+
+type NoTruncateProps = CommonProps & {
+ truncate?: false;
+};
+
+type TruncateProps = CommonProps & {
+ truncate: true;
+ expanded?: boolean;
+};
+
+// Type guard
+const isTruncateProps = (
+ props: NoTruncateProps | TruncateProps
+): props is TruncateProps => !!props.truncate;
+
+// Function overloads to accept both prop types NoTruncateProps & TruncateProps
+function Text(props: NoTruncateProps): JSX.Element;
+function Text(props: TruncateProps): JSX.Element;
+function Text(props: NoTruncateProps | TruncateProps) {
+
+ if (isTruncateProps(props)) {
+ const { children, as: Tag, truncate, expanded, ...otherProps } = props;
+
+ const classNames = truncate ? ".truncate" : "";
+
+ return (
+
+ {children}
+
+ );
+ }
+
+ const { children, as: Tag, ...otherProps } = props;
+
+ return {children};
+}
+
+Text.defaultProps = {
+ as: 'span'
+}
+```
+
+Using the Text component:
+```tsx
+const App: React.FC = () => (
+ <>
+ not truncated {/* works */}
+ truncated {/* works */}
+ truncate-able but expanded {/* works */}
+
+ {/* TS error: Property 'truncate' is missing in type '{ children: string; expanded: true; }' but required in type 'Pick'} */}
+ truncate-able but expanded
+ >
+);
+```
+
## Omit attribute from a type
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: