diff --git a/types/react/index.d.ts b/types/react/index.d.ts
index 11d879b2ff..a1e851314d 100644
--- a/types/react/index.d.ts
+++ b/types/react/index.d.ts
@@ -491,6 +491,10 @@ declare namespace React {
getDefaultProps?(): P;
}
+ type JSXElementConstructor
=
+ | ((props: P) => ReactElement | null)
+ | (new (props: P) => Component);
+
/**
* We use an intersection type to infer multiple type parameters from
* a single argument, which is useful for many top-level API defs.
@@ -719,8 +723,8 @@ declare namespace React {
* NOTE: prefer ComponentPropsWithRef, if the ref is forwarded,
* or ComponentPropsWithoutRef when refs are not supported.
*/
- type ComponentProps =
- T extends ComponentType
+ type ComponentProps> =
+ T extends JSXElementConstructor
? P
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
@@ -2629,9 +2633,20 @@ declare namespace React {
}
}
-// Declared props take priority over inferred props
+// naked 'any' type in a conditional type will short circuit and union both the then/else branches
+// so boolean is only resolved for T = any
+type IsExactlyAny = boolean extends (T extends never ? true : false) ? true : false;
+
+// Try to resolve ill-defined props like for JS users: props can be any, or sometimes objects with properties of type any
+// If props is type any, use propTypes definitions, otherwise for each `any` property of props, use the propTypes type
// If declared props have indexed properties, ignore inferred props entirely as keyof gets widened
-type MergePropTypes = P & Pick>;
+type MergePropTypes = IsExactlyAny
extends true ? T : ({
+ [K in keyof P]: IsExactlyAny
extends true
+ ? K extends keyof T
+ ? T[K]
+ : P[K]
+ : P[K]
+} & Pick>);
// Any prop that has a default prop becomes optional, but its type is unchanged
// Undeclared default props are augmented into the resulting allowable attributes
diff --git a/types/react/test/managedAttributes.tsx b/types/react/test/managedAttributes.tsx
index 1e0e8226d9..9939cffb15 100644
--- a/types/react/test/managedAttributes.tsx
+++ b/types/react/test/managedAttributes.tsx
@@ -222,3 +222,30 @@ interface LeaveMeAloneDtslint { foo: string; }
// // $ExpectError the type of ForwardRef.defaultProps stays Partial anyway even if assigned
//
// ];
+
+// const weakComponentPropTypes = {
+// foo: PropTypes.string,
+// bar: PropTypes.bool.isRequired
+// };
+// interface WeakComponentProps1 {
+// foo: any;
+// bar: number;
+// }
+// interface WeakComponentProps2 {
+// foo: string;
+// bar: any;
+// }
+
+// // $ExpectType true
+// type weakComponentTest1 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, any> extends {
+// foo?: string | null
+// bar: boolean
+// } ? true : false;
+// type weakComponentTest2 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps1> extends {
+// foo?: string | null
+// bar: number
+// } ? true : false;
+// type weakComponentTest3 = JSX.LibraryManagedAttributes<{ propTypes: typeof weakComponentPropTypes }, WeakComponentProps2> extends {
+// foo: string
+// bar: boolean
+// } ? true : false;