[react] better types for React.Children, second generic parameter for ReactElement (#32279)

* better types for react.children

* fix failing tests

* semicolons..
This commit is contained in:
Ferdy Budhidharma
2019-01-26 08:36:57 -06:00
committed by John Reilly
parent 89592f7ec3
commit afdf66dc15
6 changed files with 47 additions and 26 deletions

View File

@@ -1,5 +1,7 @@
import IdyllDocument, { IdyllDocumentProps } from "idyll-document";
import { createElement } from "react";
import { createElement, ReactElement } from "react";
// $ExpectType ReactElement<IdyllDocumentProps>
createElement<IdyllDocumentProps>(IdyllDocument);
const doc = createElement<IdyllDocumentProps>(IdyllDocument);
// $ExpectType true
type test = typeof doc extends ReactElement<IdyllDocumentProps> ? true : false;

View File

@@ -59,7 +59,7 @@ export abstract class Component<
S extends Record<string, any> = {},
C extends Record<string, any> = {}
> {
readonly props: P;
readonly props: P & { children?: InkNode };
readonly context: C;
setState(

View File

@@ -57,6 +57,10 @@ declare namespace React {
ComponentType<P>;
type ComponentType<P = {}> = ComponentClass<P> | FunctionComponent<P>;
type JSXElementConstructor<P> =
| ((props: P) => ReactElement<any> | null)
| (new (props: P) => Component<P, any>);
type Key = string | number;
interface RefObject<T> {
@@ -78,33 +82,35 @@ declare namespace React {
ref?: LegacyRef<T>;
}
interface ReactElement<P> {
type: string | ComponentClass<P> | FunctionComponent<P>;
interface ReactElement<P, T extends string | JSXElementConstructor<any> = string | JSXElementConstructor<any>> {
type: T;
props: P;
key: Key | null;
}
interface ReactComponentElement<
T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>,
P = Pick<ComponentProps<T>, Exclude<keyof ComponentProps<T>, 'key' | 'ref'>>
> extends ReactElement<P, T> { }
/**
* @deprecated Please use `FunctionComponentElement`
*/
type SFCElement<P> = FunctionComponentElement<P>;
interface FunctionComponentElement<P> extends ReactElement<P> {
type: FunctionComponent<P>;
interface FunctionComponentElement<P> extends ReactElement<P, FunctionComponent<P>> {
ref?: 'ref' extends keyof P ? P extends { ref?: infer R } ? R : never : never;
}
type CElement<P, T extends Component<P, ComponentState>> = ComponentElement<P, T>;
interface ComponentElement<P, T extends Component<P, ComponentState>> extends ReactElement<P> {
type: ComponentClass<P>;
interface ComponentElement<P, T extends Component<P, ComponentState>> extends ReactElement<P, ComponentClass<P>> {
ref?: LegacyRef<T>;
}
type ClassicElement<P> = CElement<P, ClassicComponent<P, ComponentState>>;
// string fallback for custom web-components
interface DOMElement<P extends HTMLAttributes<T> | SVGAttributes<T>, T extends Element> extends ReactElement<P> {
type: string;
interface DOMElement<P extends HTMLAttributes<T> | SVGAttributes<T>, T extends Element> extends ReactElement<P, string> {
ref: LegacyRef<T>;
}
@@ -491,10 +497,6 @@ 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.
@@ -2586,12 +2588,11 @@ declare namespace React {
// ----------------------------------------------------------------------
interface ReactChildren {
map<T, C extends ReactElement<any>>(children: C[], fn: (child: C, index: number) => T): T[];
map<T>(children: ReactNode, fn: (child: ReactChild, index: number) => T): T[];
forEach(children: ReactNode, fn: (child: ReactChild, index: number) => void): void;
count(children: ReactNode): number;
only(children: ReactNode): ReactElement<any>;
toArray(children: ReactNode): ReactChild[];
map<T, C>(children: C | C[], fn: (child: C, index: number) => T): T[];
forEach<C>(children: C | C[], fn: (child: C, index: number) => void): void;
count(children: any): number;
only<C>(children: C): C extends any[] ? never : C;
toArray<C>(children: C | C[]): C[];
}
//
@@ -2670,7 +2671,7 @@ type ReactManagedAttributes<C, P> = C extends { propTypes: infer T; defaultProps
declare global {
namespace JSX {
// tslint:disable-next-line:no-empty-interface
interface Element extends React.ReactElement<any> { }
interface Element extends React.ReactElement<any, any> { }
interface ElementClass extends React.Component<any> {
render(): React.ReactNode;
}

View File

@@ -488,7 +488,7 @@ DOM.svg({
// --------------------------------------------------------------------------
const mappedChildrenArray: number[] =
React.Children.map<number>(children, (child) => 42);
React.Children.map(children, (child: any) => 42);
const childrenArray: Array<React.ReactElement<{ p: number }>> = children;
const mappedChildrenArrayWithKnownChildren: number[] =
React.Children.map(childrenArray, (child) => child.props.p);
@@ -498,6 +498,23 @@ let onlyChild: React.ReactElement<any> = React.Children.only(DOM.div()); // ok
onlyChild = React.Children.only([null, [[["Hallo"], true]], false]); // error
const childrenToArray: React.ReactChild[] = React.Children.toArray(children);
declare const numberChildren: number[];
declare const elementChildren: JSX.Element[];
declare const mixedChildren: Array<JSX.Element | string>;
declare const singlePluralChildren: JSX.Element | JSX.Element[];
declare const renderPropsChildren: () => JSX.Element;
// $ExpectType number[]
const mappedChildrenArray2 = React.Children.map(numberChildren, num => num);
// $ExpectType Element[]
const mappedChildrenArray3 = React.Children.map(elementChildren, element => element);
// $ExpectType (string | Element)[]
const mappedChildrenArray4 = React.Children.map(mixedChildren, elementOrString => elementOrString);
// $ExpectType (string | number | null)[]
const mappedChildrenArray5 = React.Children.map(singlePluralChildren, element => element.key);
// $ExpectType string[]
const mappedChildrenArray6 = React.Children.map(renderPropsChildren, element => element.name);
//
// Example from http://facebook.github.io/react/
// --------------------------------------------------------------------------
@@ -774,7 +791,7 @@ class RenderChildren extends React.Component {
const Memoized1 = React.memo(function Foo(props: { foo: string }) { return null; });
React.createElement(Memoized1, { foo: 'string' });
const Memoized2 = React.memo(
const Memoized2 = React.memo<{ bar: string }>(
function Bar(props: { bar: string }) { return null; },
(prevProps, nextProps) => prevProps.bar === nextProps.bar
);

View File

@@ -361,7 +361,7 @@ interface TestPropTypesProps3 {
const testPropTypes = {
foo: PropTypes.string
};
type DeclaredPropTypes<P> = Required<Exclude<React.ComponentType<P>['propTypes'], undefined>>;
type DeclaredPropTypes<P> = Required<Exclude<React.FunctionComponent<P>['propTypes'], undefined>>;
// $ExpectType false
type propTypesTest = typeof testPropTypes extends DeclaredPropTypes<TestPropTypesProps> ? true : false;
// $ExpectType true

View File

@@ -10,6 +10,7 @@ import { StyledComponent, AnyStyledComponent, CSSObject, InterpolationFunction }
declare const BaseModalBackground: StyledComponent<'div', any>;
interface ModalProps {
children?: React.ReactNode;
isOpen: boolean;
allowScroll?: boolean;
afterOpen?: () => void;