From de1592ac8aa4d52dda9a6da8f0c05ac746c97062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Wed, 18 Mar 2020 14:51:16 +0100 Subject: [PATCH 01/10] [react] Port ElementRef utility type from Flow https://flow.org/en/docs/react/types/#toc-react-elementref --- types/react/index.d.ts | 26 ++++++++++++++++++++++++++ types/react/test/index.ts | 5 +++++ 2 files changed, 31 insertions(+) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 645ffafc679015..6c2ea4e389d981 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -90,6 +90,32 @@ declare namespace React { 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`. + * + * `C` must be the type _of_ a React component so you need to use typeof as in React.ElementRef. + */ + type ElementRef< + C extends + | ComponentClass + | FunctionComponent + | keyof JSX.IntrinsicElements + > = C extends ComponentClass + ? InstanceType + : C extends FunctionComponent + ? undefined + : C extends keyof JSX.IntrinsicElements + ? JSX.IntrinsicElements[C] extends DOMAttributes + ? E + : never + : never; type ComponentState = any; diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 87aeb5494f0476..306096c64c3a41 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -440,6 +440,11 @@ function RefCarryingComponent() { ); } +type RefComponentAsRef = React.ElementRef; // $ExpectType RefComponent +type RefCarryingComponentAsRef = React.ElementRef; // $ExpectType undefined +type HTMLIntrinsicAsRef = React.ElementRef<'div'>; // $ExpectType HTMLDivElement +type SVGIntrinsicAsRef = React.ElementRef<'svg'>; // $ExpectType SVGSVGElement + // // Attributes // -------------------------------------------------------------------------- From d66bc4c12aca2c603a8bf265ae45ec4dfc83e234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Wed, 18 Mar 2020 20:59:14 +0100 Subject: [PATCH 02/10] [react] Use least amount of required typing --- types/react/index.d.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 6c2ea4e389d981..1fb918c7b7ede3 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -104,12 +104,12 @@ declare namespace React { */ type ElementRef< C extends - | ComponentClass - | FunctionComponent + | { new (props: any): React.Component } + | (() => JSX.Element) | keyof JSX.IntrinsicElements - > = C extends ComponentClass + > = C extends { new (props: any): React.Component } ? InstanceType - : C extends FunctionComponent + : C extends (() => JSX.Element) ? undefined : C extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[C] extends DOMAttributes From b92bf4026dc71e9981cc5a89a47278e515385fe6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Wed, 18 Mar 2020 21:52:59 +0100 Subject: [PATCH 03/10] [react] Support forwarded refs with ElementRef --- types/react/index.d.ts | 14 ++++++++++++-- types/react/test/index.ts | 1 + 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 1fb918c7b7ede3..14e5fbdd48cbf9 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -99,15 +99,21 @@ declare namespace React { * (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 - | { new (props: any): React.Component } + | { new (props: any): Component } | (() => JSX.Element) | keyof JSX.IntrinsicElements - > = C extends { new (props: any): React.Component } + | ForwardRefExoticComponent + > = C extends { new (props: any): Component } ? InstanceType : C extends (() => JSX.Element) ? undefined @@ -115,6 +121,10 @@ declare namespace React { ? JSX.IntrinsicElements[C] extends DOMAttributes ? E : never + : C extends ForwardRefExoticComponent + ? FP extends RefAttributes + ? FC + : never : never; type ComponentState = any; diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 306096c64c3a41..f2623f3f241a9b 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -444,6 +444,7 @@ type RefComponentAsRef = React.ElementRef; // $ExpectType R type RefCarryingComponentAsRef = React.ElementRef; // $ExpectType undefined type HTMLIntrinsicAsRef = React.ElementRef<'div'>; // $ExpectType HTMLDivElement type SVGIntrinsicAsRef = React.ElementRef<'svg'>; // $ExpectType SVGSVGElement +type ForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent // // Attributes From 4e5d62582af1c3473c60ff5fbe3808819e60a33c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Wed, 18 Mar 2020 22:01:35 +0100 Subject: [PATCH 04/10] [react] Use correct function component return type --- types/react/index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 14e5fbdd48cbf9..13979afe6aa781 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -103,19 +103,19 @@ declare namespace React { * 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 | { new (props: any): Component } - | (() => JSX.Element) + | (() => ReactElement | null) | keyof JSX.IntrinsicElements | ForwardRefExoticComponent > = C extends { new (props: any): Component } ? InstanceType - : C extends (() => JSX.Element) + : C extends (() => ReactElement | null) ? undefined : C extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[C] extends DOMAttributes From 6b540066520436d5b712829c9d9612f7df3b8fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Thu, 19 Mar 2020 15:20:33 +0100 Subject: [PATCH 05/10] [react] Add ElementRef test for memoized ref forwarding component --- types/react/test/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/types/react/test/index.ts b/types/react/test/index.ts index f2623f3f241a9b..6083bcc725d310 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -440,11 +440,14 @@ function RefCarryingComponent() { ); } +const MemoizedForwardingRefComponent = React.memo(ForwardingRefComponent); + type RefComponentAsRef = React.ElementRef; // $ExpectType RefComponent type RefCarryingComponentAsRef = 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 // // Attributes From 5fa3e1736ae3974a59eb9fc959164c10835310e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Thu, 19 Mar 2020 16:08:05 +0100 Subject: [PATCH 06/10] [react] Add ElementRef test for lazy component --- types/react/test/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 6083bcc725d310..13cfe553e4086e 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -441,6 +441,7 @@ function RefCarryingComponent() { } const MemoizedForwardingRefComponent = React.memo(ForwardingRefComponent); +const LazyRefComponent = React.lazy(() => Promise.resolve({ default: RefComponent })); type RefComponentAsRef = React.ElementRef; // $ExpectType RefComponent type RefCarryingComponentAsRef = React.ElementRef; // $ExpectType undefined @@ -448,6 +449,7 @@ 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 LazyForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent // // Attributes From 333a97a5be868b69db397a0924495e4e8bb9ee4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Thu, 19 Mar 2020 17:04:28 +0100 Subject: [PATCH 07/10] [react] Match function components with props --- types/react/index.d.ts | 16 ++++++++-------- types/react/test/index.ts | 9 +++++---- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 13979afe6aa781..fc26533260913b 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -109,22 +109,22 @@ declare namespace React { */ type ElementRef< C extends + | ForwardRefExoticComponent | { new (props: any): Component } - | (() => ReactElement | null) + | ((props: any, context?: any) => ReactElement | null) | keyof JSX.IntrinsicElements - | ForwardRefExoticComponent - > = C extends { new (props: any): Component } + > = C extends ForwardRefExoticComponent + ? FP extends RefAttributes + ? FC + : never + : C extends { new (props: any): Component } ? InstanceType - : C extends (() => ReactElement | null) + : C extends ((props: any, context?: any) => ReactElement | null) ? undefined : C extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[C] extends DOMAttributes ? E : never - : C extends ForwardRefExoticComponent - ? FP extends RefAttributes - ? FC - : never : never; type ComponentState = any; diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 13cfe553e4086e..278e9d744cd029 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -441,15 +441,16 @@ function RefCarryingComponent() { } const MemoizedForwardingRefComponent = React.memo(ForwardingRefComponent); -const LazyRefComponent = React.lazy(() => Promise.resolve({ default: RefComponent })); +const LazyComponent = React.lazy(() => Promise.resolve({ default: RefComponent })); -type RefComponentAsRef = React.ElementRef; // $ExpectType RefComponent -type RefCarryingComponentAsRef = React.ElementRef; // $ExpectType undefined +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 LazyForwardingRefComponentAsRef = React.ElementRef; // $ExpectType RefComponent +type LazyComponentAsRef = React.ElementRef; // $ExpectType RefComponent // // Attributes From ce830707266deab17289b07b57df0e37e03e04ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Thu, 19 Mar 2020 18:17:26 +0100 Subject: [PATCH 08/10] [react] Fix test-case by moving type around --- types/react/index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index fc26533260913b..8486cf8c75e699 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -82,8 +82,6 @@ declare namespace React { | ((props: P) => ReactElement | null) | (new (props: P) => Component); - type Key = string | number; - interface RefObject { readonly current: T | null; } @@ -129,6 +127,8 @@ declare namespace React { 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. From 31151a2abb3a71b8308d89d1a617a802b86eae99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Thu, 19 Mar 2020 19:12:27 +0100 Subject: [PATCH 09/10] [react] Fix flaky test-case --- types/react/test/index.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/types/react/test/index.ts b/types/react/test/index.ts index 278e9d744cd029..b74bb5f98b1819 100644 --- a/types/react/test/index.ts +++ b/types/react/test/index.ts @@ -554,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 From bce5bbc752f98d07e55e5b29304de04d03a723a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eloy=20Dur=C3=A1n?= Date: Fri, 20 Mar 2020 16:05:34 +0100 Subject: [PATCH 10/10] [recharts] Remove deprecated CSS properties These have been removed from `lib.dom.d.ts`: https://github.com/microsoft/TypeScript/pull/37464/files#r395692943 --- types/recharts/index.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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';