From 4dd9510a504875fb79a399d468e8f39e892af617 Mon Sep 17 00:00:00 2001 From: Jessica Date: Fri, 23 Nov 2018 15:05:46 +0900 Subject: [PATCH 1/3] Strengthen type of ReactType to also validate intrinsic elements --- types/react/index.d.ts | 12 ++++++++---- types/react/test/tsx.tsx | 7 +++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/types/react/index.d.ts b/types/react/index.d.ts index 25f3c710ae..522a106944 100644 --- a/types/react/index.d.ts +++ b/types/react/index.d.ts @@ -50,7 +50,11 @@ declare namespace React { // React Elements // ---------------------------------------------------------------------- - type ReactType

= string | ComponentType

; + type ReactType

= + { + [K in keyof JSX.IntrinsicElements]: P extends JSX.IntrinsicElements[K] ? K : never + }[keyof JSX.IntrinsicElements] | + ComponentType

; type ComponentType

= ComponentClass

| FunctionComponent

; type Key = string | number; @@ -713,17 +717,17 @@ declare namespace React { * NOTE: prefer ComponentPropsWithRef, if the ref is forwarded, * or ComponentPropsWithoutRef when refs are not supported. */ - type ComponentProps> = + type ComponentProps = T extends ComponentType ? P : T extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[T] : {}; - type ComponentPropsWithRef> = + type ComponentPropsWithRef = T extends ComponentClass ? PropsWithoutRef

& RefAttributes> : PropsWithRef>; - type ComponentPropsWithoutRef> = + type ComponentPropsWithoutRef = PropsWithoutRef>; // will show `Memo(${Component.displayName || Component.name})` in devtools by default, diff --git a/types/react/test/tsx.tsx b/types/react/test/tsx.tsx index f344fb2838..a8ca2ae1ea 100644 --- a/types/react/test/tsx.tsx +++ b/types/react/test/tsx.tsx @@ -325,3 +325,10 @@ type ImgPropsWithRefRef = ImgPropsWithRef['ref']; type ImgPropsWithoutRef = React.ComponentPropsWithoutRef<'img'>; // $ExpectType false type ImgPropsHasRef = 'ref' extends keyof ImgPropsWithoutRef ? true : false; + +const HasClassName: React.ReactType<{ className?: string }> = 'a'; +const HasFoo: React.ReactType<{ foo: boolean }> = 'a'; // $ExpectError +const HasFoo2: React.ReactType<{ foo: boolean }> = (props: { foo: boolean }) => null; +const HasFoo3: React.ReactType<{ foo: boolean }> = (props: { foo: string }) => null; // $ExpectError +const HasHref: React.ReactType<{ href?: string }> = 'a'; +const HasHref2: React.ReactType<{ href?: string }> = 'div'; // $ExpectError From cc8f6fcd7c73631c582b611359fadd0241e81cce Mon Sep 17 00:00:00 2001 From: Jessica Date: Sat, 24 Nov 2018 00:08:22 +0900 Subject: [PATCH 2/3] Fix incorrect type caught by stricter ReactType --- types/react-native-scrollable-tab-view/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/react-native-scrollable-tab-view/index.d.ts b/types/react-native-scrollable-tab-view/index.d.ts index 9be23aa66c..d992012856 100644 --- a/types/react-native-scrollable-tab-view/index.d.ts +++ b/types/react-native-scrollable-tab-view/index.d.ts @@ -125,7 +125,7 @@ export default class ScrollableTabView extends React.Component = T & { - tabLabel: React.ReactType; + tabLabel: React.ReactChild; }; export interface DefaultTabBarProps { From 8beb9e99a78464b855da7011872ba15575e023d6 Mon Sep 17 00:00:00 2001 From: Jessica Date: Sat, 24 Nov 2018 00:08:43 +0900 Subject: [PATCH 3/3] Fix test, add test to demonstrate how custom elements will work --- .../react-test-renderer-tests.ts | 8 ++++---- types/react/test/tsx.tsx | 13 +++++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/types/react-test-renderer/react-test-renderer-tests.ts b/types/react-test-renderer/react-test-renderer-tests.ts index 45568c81a7..83dc510e2b 100644 --- a/types/react-test-renderer/react-test-renderer-tests.ts +++ b/types/react-test-renderer/react-test-renderer-tests.ts @@ -43,14 +43,14 @@ function testInstance(inst: ReactTestInstance) { inst.props = { prop1: "p", }; - inst.type = "t"; - testInstance(inst.find(n => n.type === "t")); + inst.type = "a"; + testInstance(inst.find(n => n.type === "a")); testInstance(inst.findByProps({ prop1: "p" })); - testInstance(inst.findByType("t")); + testInstance(inst.findByType("a")); testInstance(inst.findByType(TestComponent)); inst.findAll(n => n.type === "t", { deep: true }).map(testInstance); inst.findAllByProps({ prop1: "p" }, { deep: true }).map(testInstance); - inst.findAllByType("t", { deep: true }).map(testInstance); + inst.findAllByType("a", { deep: true }).map(testInstance); inst.findAllByType(TestComponent, { deep: true }).map(testInstance); } diff --git a/types/react/test/tsx.tsx b/types/react/test/tsx.tsx index a8ca2ae1ea..60fcb6dccc 100644 --- a/types/react/test/tsx.tsx +++ b/types/react/test/tsx.tsx @@ -332,3 +332,16 @@ const HasFoo2: React.ReactType<{ foo: boolean }> = (props: { foo: boolean }) => const HasFoo3: React.ReactType<{ foo: boolean }> = (props: { foo: string }) => null; // $ExpectError const HasHref: React.ReactType<{ href?: string }> = 'a'; const HasHref2: React.ReactType<{ href?: string }> = 'div'; // $ExpectError + +const CustomElement: React.ReactType = 'my-undeclared-element'; // $ExpectError + +// custom elements now need to be declared as intrinsic elements +declare global { + namespace JSX { + interface IntrinsicElements { + 'my-declared-element': {}; + } + } +} + +const CustomElement2: React.ReactType = 'my-declared-element';