[react] better prop typing for JS users (#32182)

* better prop resolutions

* lint rules
This commit is contained in:
Ferdy Budhidharma
2019-01-16 07:30:26 +00:00
committed by John Reilly
parent 7b2a31abb1
commit a6fd2ee6ac
2 changed files with 46 additions and 4 deletions
+19 -4
View File
@@ -491,6 +491,10 @@ declare namespace React {
getDefaultProps?(): P;
}
type JSXElementConstructor<P> =
| ((props: P) => ReactElement<any> | null)
| (new (props: P) => Component<P, any>);
/**
* 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 ReactType> =
T extends ComponentType<infer P>
type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> =
T extends JSXElementConstructor<infer P>
? 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<T> = 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, T> = P & Pick<T, Exclude<keyof T, keyof P>>;
type MergePropTypes<P, T> = IsExactlyAny<P> extends true ? T : ({
[K in keyof P]: IsExactlyAny<P[K]> extends true
? K extends keyof T
? T[K]
: P[K]
: P[K]
} & Pick<T, Exclude<keyof T, keyof P>>);
// Any prop that has a default prop becomes optional, but its type is unchanged
// Undeclared default props are augmented into the resulting allowable attributes
+27
View File
@@ -222,3 +222,30 @@ interface LeaveMeAloneDtslint { foo: string; }
// // $ExpectError the type of ForwardRef.defaultProps stays Partial<P> anyway even if assigned
// <ForwardRef str='abc' />
// ];
// 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;