diff --git a/types/react/index.d.ts b/types/react/index.d.ts
index 645ffafc679015..8486cf8c75e699 100644
--- a/types/react/index.d.ts
+++ b/types/react/index.d.ts
@@ -82,17 +82,53 @@ declare namespace React {
| ((props: P) => ReactElement | null)
| (new (props: P) => Component
);
- type Key = string | number;
-
interface RefObject {
readonly current: T | null;
}
type RefCallback = { bivarianceHack(instance: T | null): void }["bivarianceHack"];
type Ref = RefCallback | RefObject | null;
type LegacyRef = string | Ref;
+ /**
+ * Gets the instance type for a React element. The instance will be different for various component types:
+ *
+ * - React class components will be the class instance. So if you had `class Foo extends React.Component<{}> {}`
+ * and used `React.ElementRef` then the type would be the instance of `Foo`.
+ * - React stateless functional components do not have a backing instance and so `React.ElementRef`
+ * (when `Bar` is `function Bar() {}`) will give you the `undefined` type.
+ * - JSX intrinsics like `div` will give you their DOM instance. For `React.ElementRef<'div'>` that would be
+ * `HTMLDivElement`. For `React.ElementRef<'input'>` that would be `HTMLInputElement`.
+ * - React stateless functional components that forward a `ref` will give you the `ElementRef` of the forwarded
+ * to component.
+ *
+ * `C` must be the type _of_ a React component so you need to use typeof as in React.ElementRef.
+ *
+ * @todo In Flow, this works a little different with forwarded refs and the `AbstractComponent` that
+ * `React.forwardRef()` returns.
+ */
+ type ElementRef<
+ C extends
+ | ForwardRefExoticComponent
+ | { new (props: any): Component }
+ | ((props: any, context?: any) => ReactElement | null)
+ | keyof JSX.IntrinsicElements
+ > = C extends ForwardRefExoticComponent
+ ? FP extends RefAttributes
+ ? FC
+ : never
+ : C extends { new (props: any): Component }
+ ? InstanceType
+ : C extends ((props: any, context?: any) => ReactElement | null)
+ ? undefined
+ : C extends keyof JSX.IntrinsicElements
+ ? JSX.IntrinsicElements[C] extends DOMAttributes
+ ? E
+ : never
+ : never;
type ComponentState = any;
+ type Key = string | number;
+
/**
* @internal You shouldn't need to use this type since you never see these attributes
* inside your component or have to validate them.
diff --git a/types/react/test/index.ts b/types/react/test/index.ts
index 87aeb5494f0476..b74bb5f98b1819 100644
--- a/types/react/test/index.ts
+++ b/types/react/test/index.ts
@@ -440,6 +440,18 @@ function RefCarryingComponent() {
);
}
+const MemoizedForwardingRefComponent = React.memo(ForwardingRefComponent);
+const LazyComponent = React.lazy(() => Promise.resolve({ default: RefComponent }));
+
+type ClassComponentAsRef = React.ElementRef; // $ExpectType RefComponent
+type FunctionComponentWithoutPropsAsRef = React.ElementRef; // $ExpectType undefined
+type FunctionComponentWithPropsAsRef = React.ElementRef; // $ExpectType undefined
+type HTMLIntrinsicAsRef = React.ElementRef<'div'>; // $ExpectType HTMLDivElement
+type SVGIntrinsicAsRef = React.ElementRef<'svg'>; // $ExpectType SVGSVGElement
+type ForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent
+type MemoizedForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent
+type LazyComponentAsRef = React.ElementRef; // $ExpectType RefComponent
+
//
// Attributes
// --------------------------------------------------------------------------
@@ -542,8 +554,11 @@ const mappedChildrenArray2 = React.Children.map(numberChildren, num => num);
const mappedChildrenArray3 = React.Children.map(elementChildren, element => element);
// $ExpectType (string | Element)[]
const mappedChildrenArray4 = React.Children.map(mixedChildren, elementOrString => elementOrString);
-// $ExpectType Key[]
+// This test uses a conditional type because otherwise it gets flaky and can resolve to either Key or ReactText, both
+// of which are aliases for `string | number`.
const mappedChildrenArray5 = React.Children.map(singlePluralChildren, element => element.key);
+// $ExpectType true
+type mappedChildrenArray5Type = typeof mappedChildrenArray5 extends React.Key[] ? true : false;
// $ExpectType string[]
const mappedChildrenArray6 = React.Children.map(renderPropsChildren, element => element.name);
// The return type may not be an array
diff --git a/types/recharts/index.d.ts b/types/recharts/index.d.ts
index 15d14af0096121..2ffcce5b70730e 100644
--- a/types/recharts/index.d.ts
+++ b/types/recharts/index.d.ts
@@ -61,9 +61,9 @@ export type ReferenceLinePosition = 'start' | 'middle' | 'end';
export type PickedCSSStyleDeclarationKeys =
'alignmentBaseline' | 'baselineShift' | 'clip' | 'clipPath' | 'clipRule' | 'color' |
'colorInterpolationFilters' | 'cursor' | 'direction' | 'display' | 'dominantBaseline' |
- 'enableBackground' | 'fill' | 'fillRule' | 'filter' | 'floodColor' |
+ 'fill' | 'fillRule' | 'filter' | 'floodColor' |
'floodOpacity' | 'font' | 'fontFamily' | 'fontStretch' | 'fontStyle' | 'fontVariant' |
- 'glyphOrientationHorizontal' | 'glyphOrientationVertical' | 'letterSpacing' | 'lightingColor' |
+ 'glyphOrientationVertical' | 'letterSpacing' | 'lightingColor' |
'markerEnd' | 'markerMid' | 'markerStart' | 'mask' | 'overflow' | 'pointerEvents' |
'stopColor' | 'strokeDasharray' | 'strokeLinecap' | 'strokeLinejoin' | 'textAnchor' |
'textDecoration' | 'unicodeBidi' | 'visibility' | 'writingMode' | 'transform';