Skip to content

Commit 9d417a3

Browse files
SamChou19815facebook-github-bot
authored andcommitted
[flow][ref-as-prop] Make React.ComponentType's ref prop behave like ref?: empty
Summary: This diff continues to make progress to move towards full support for ref-as-prop instead of staying in the current half-done state where the ref prop is still too special. In this diff, we aim to make `React.ComponentType<{}>` behave like `component(ref?: empty)` for subtyping. `React.ComponentType<...>` is always supposed to be the top type in terms of component instance. Previously before ref-as-prop, it behaves like `component(ref?: React.RefSetter<mixed>)` because the ref prop is special and it's always a ref-setter, but with ref-as-prop, it should be the same as all other props, similar to how `React.ComponentType<empty>` is the top type of all components. I was able to make the change for most of the subtyping rule, except for the `React.ComponentType<{}> ~> component(ref: ref_prop)` one because the error diff is too big (see D69706346) and we cannot fix the codebase without this diff as a pre-req. This is relatively harmless, because ref-prop is still kinda special. But once a new Flow version with this diff is deployed, we will be able to start replacing most of the generic `component(ref: React.RefSetter<GenericInstance>, ...Props)` with `component(ref: GenericRefProp, ...Props)` so that we can continue the rollout. Changelog: [internal] Reviewed By: jbrown215 Differential Revision: D69706347 fbshipit-source-id: 8c8f2ebad492f9ba8e8e2dead6c8b253a8e2ba84
1 parent 6eac3bb commit 9d417a3

File tree

5 files changed

+17
-132
lines changed

5 files changed

+17
-132
lines changed

src/typing/subtyping_kit.ml

Lines changed: 13 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -593,22 +593,20 @@ module Make (Flow : INPUT) : OUTPUT = struct
593593
(* ref prop is contravariantly typed. We need to flip the flow. *)
594594
rec_flow_t cx trace ~use_op (r, l)
595595
(* component() (treated as component(ref?: React.RefSetter<void>))
596-
* ~> React.ComponentType<{}> (equivalent to component(ref?: React.RefSetter<mixed>))
596+
* ~> React.ComponentType<{}> (equivalent to component(ref?: empty))
597597
*
598-
* This is allowed since void ~> mixed, but only temporarily, because `component()` being treated
599-
* as component(ref?: React.RefSetter<void>) is a result of intermediate ref-as-prop migration.
600-
* In the future, it should be banned, in the same way that
601-
* `{+ref?: React.RefSetter<mixed>} ~> {}` is banned. *)
598+
* Technically `{+ref?: empty} ~> {}` should banned, but it's relatively benign,
599+
* so we allow it for now. *)
602600
| (ComponentInstanceOmitted _, ComponentInstanceTopType _) -> ()
603-
(* React.ComponentType<{}> (equivalent to component(ref?: React.RefSetter<mixed>)) ~> component()
601+
(* React.ComponentType<{}> (equivalent to component(ref?: empty)) ~> component()
604602
*
605603
* If `component()` is treated as component(ref?: React.RefSetter<void>), it will fail due to
606604
* `mixed ~> void`. However, this treatement is temporary while we are moving to full ref-as-prop
607605
* model, in the ref as prop model, the subtyping behavior should be
608606
*
609-
* React.ComponentType<{}> (equivalent to component(ref?: React.RefSetter<mixed>)) ~> component()
610-
* -> component(ref?: React.RefSetter<mixed>) ~> component()
611-
* -> {} ~> {+ref?: React.RefSetter<mixed>}
607+
* React.ComponentType<{}> (equivalent to component(ref?: empty)) ~> component()
608+
* -> component(ref?: empty) ~> component()
609+
* -> {} ~> {+ref?: empty}
612610
* -> OK!
613611
* *)
614612
| (ComponentInstanceTopType _, ComponentInstanceOmitted _) -> ()
@@ -621,14 +619,8 @@ module Make (Flow : INPUT) : OUTPUT = struct
621619
* Allowed since ``{} ~> {+ref?: ref_prop} *)
622620
| (ComponentInstanceAvailableAsRefSetterProp _, ComponentInstanceOmitted _) -> ()
623621
(* component(ref: ref_prop)
624-
* ~> React.ComponentType<{}> (equivalent to component(ref?: React.RefSetter<mixed>)) *)
625-
| (ComponentInstanceAvailableAsRefSetterProp ref_prop, ComponentInstanceTopType r_mixed) ->
626-
let instance = MixedT.why r_mixed in
627-
(match try_extract_instance_of_ref_setter cx trace ref_prop with
628-
| Some t -> rec_flow_t cx trace ~use_op (t, instance)
629-
| None ->
630-
(* RefSetter is contravariantly typed. We need to flip the flow. *)
631-
rec_flow_t cx trace ~use_op (react_ref_setter_of cx instance, ref_prop))
622+
* ~> React.ComponentType<{}> (equivalent to component(ref?: empty)) *)
623+
| (ComponentInstanceAvailableAsRefSetterProp _, ComponentInstanceTopType _) -> ()
632624
(* component() (equivalent to component(ref?: React.RefSetter<void>))
633625
* ~> component(ref: ref_prop) *)
634626
| (ComponentInstanceOmitted r, ComponentInstanceAvailableAsRefSetterProp ref_prop) ->
@@ -637,8 +629,10 @@ module Make (Flow : INPUT) : OUTPUT = struct
637629
| None ->
638630
(* RefSetter is contravariantly typed. We need to flip the flow. *)
639631
rec_flow_t cx trace ~use_op (ref_prop, react_ref_setter_of cx (DefT (r, VoidT))))
640-
(* React.ComponentType<{}> (equivalent to component(ref?: React.RefSetter<mixed>))
641-
* ~> component(ref: ref_prop) *)
632+
(* React.ComponentType<{}> (equivalent to component(ref?: empty)) ~> component(ref: ref_prop)
633+
*
634+
* Should be banned since ref_prop ~> empty will almost always fail, but we allow it for now
635+
* since it's needed for incremental migration and it's relatively harmless. *)
642636
| (ComponentInstanceTopType r_mixed, ComponentInstanceAvailableAsRefSetterProp ref_prop) ->
643637
let instance = MixedT.why r_mixed in
644638
(match try_extract_instance_of_ref_setter cx trace ref_prop with

src/typing/ty_normalizer.ml

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1250,16 +1250,7 @@ module Make (I : INPUT) : S = struct
12501250
match instance with
12511251
| Type.ComponentInstanceOmitted _ -> return None
12521252
| Type.ComponentInstanceAvailableAsRefSetterProp t -> type__ ~env t >>| Base.Option.some
1253-
| Type.ComponentInstanceTopType _ ->
1254-
return
1255-
(Some
1256-
(Ty.Generic
1257-
( Ty_symbol.builtin_symbol (Reason.OrdinaryName "React.RefSetter"),
1258-
Ty.TypeAliasKind,
1259-
Some [Ty.Top]
1260-
)
1261-
)
1262-
)
1253+
| Type.ComponentInstanceTopType _ -> return (Some Ty.(Bot EmptyType))
12631254
in
12641255
let%bind renders =
12651256
match renders with

src/typing/type_sig_merge.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,7 @@ and merge_annot env file = function
953953
reason
954954
in
955955
let instance =
956-
Type.ComponentInstanceTopType (mk_default_type_argument_reason_at_position Reason.RMixed 2)
956+
Type.ComponentInstanceTopType (mk_default_type_argument_reason_at_position Reason.REmpty 2)
957957
in
958958
let renders =
959959
let reason =

tests/component_syntax/component_syntax.exp

Lines changed: 1 addition & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -178,31 +178,6 @@ References:
178178
^^^^^^^^^^^^^^^^^^^ [3]
179179

180180

181-
Error ----------------------------------------------------------------------------------------------- annotation.js:49:2
182-
183-
Cannot instantiate `React.Element` because in type argument `ElementType`: [incompatible-type-arg]
184-
- Either component [1] is incompatible with string [2].
185-
- Or object type [3] is incompatible with number [4].
186-
187-
annotation.js:49:2
188-
49| <InlineRef ref={1} />; // error: string and number refs are still not allowed
189-
^^^^^^^^^
190-
191-
References:
192-
annotation.js:48:24
193-
48| declare var InlineRef: component(ref: number); // error
194-
^^^^^^^^^^^^^^^^^^^^^^ [1]
195-
<BUILTINS>/react.js:131:5
196-
131| | string
197-
^^^^^^ [2]
198-
<BUILTINS>/react.js:192:5
199-
192| | { -current: T | null, ... }
200-
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
201-
annotation.js:48:39
202-
48| declare var InlineRef: component(ref: number); // error
203-
^^^^^^ [4]
204-
205-
206181
Error ---------------------------------------------------------------------------------------------- annotation.js:51:40
207182

208183
Components do not support ref properties [1] within spreads [2] [invalid-component-prop]
@@ -1209,31 +1184,6 @@ References:
12091184
^^^^^^^^^^^^^^^^^^^ [3]
12101185

12111186

1212-
Error ------------------------------------------------------------------------------------------------- declared.js:51:2
1213-
1214-
Cannot instantiate `InlineRef` element because in type argument `ElementType`: [incompatible-type-arg]
1215-
- Either component InlineRef [1] is incompatible with string [2].
1216-
- Or object type [3] is incompatible with number [4].
1217-
1218-
declared.js:51:2
1219-
51| <InlineRef ref={1} />; // error: string and number refs are still not allowed
1220-
^^^^^^^^^
1221-
1222-
References:
1223-
declared.js:50:1
1224-
50| declare component InlineRef(ref: number); // error
1225-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1]
1226-
<BUILTINS>/react.js:131:5
1227-
131| | string
1228-
^^^^^^ [2]
1229-
<BUILTINS>/react.js:192:5
1230-
192| | { -current: T | null, ... }
1231-
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
1232-
declared.js:50:34
1233-
50| declare component InlineRef(ref: number); // error
1234-
^^^^^^ [4]
1235-
1236-
12371187
Error ------------------------------------------------------------------------------------------------ declared.js:53:35
12381188

12391189
Components do not support ref properties [1] within spreads [2] [invalid-component-prop]
@@ -1901,31 +1851,6 @@ References:
19011851
^^^^^^ [2]
19021852

19031853

1904-
Error --------------------------------------------------------------------------------------------------- import.js:37:2
1905-
1906-
Cannot instantiate `NoRef` element because in type argument `ElementType`: [incompatible-type-arg]
1907-
- Either component NoRef [1] is incompatible with string [2].
1908-
- Or object type [3] is incompatible with string [4].
1909-
1910-
import.js:37:2
1911-
37| <NoRef /> // error again because ref in NoRef is bad
1912-
^^^^^
1913-
1914-
References:
1915-
names.js:22:8
1916-
22| export component NoRef(ref: string) { return <div /> }; // Error
1917-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1]
1918-
<BUILTINS>/react.js:131:5
1919-
131| | string
1920-
^^^^^^ [2]
1921-
<BUILTINS>/react.js:192:5
1922-
192| | { -current: T | null, ... }
1923-
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
1924-
names.js:22:29
1925-
22| export component NoRef(ref: string) { return <div /> }; // Error
1926-
^^^^^^ [4]
1927-
1928-
19291854
Error --------------------------------------------------------------------------------------------------- import.js:42:2
19301855

19311856
Cannot create `ExportType` element because property `x` is missing in props [1] but exists in props of component [2].
@@ -2240,31 +2165,6 @@ References:
22402165
^^^^^^^^^^^^^^^^^^^ [3]
22412166

22422167

2243-
Error ---------------------------------------------------------------------------------------------------- names.js:23:2
2244-
2245-
Cannot instantiate `NoRef` element because in type argument `ElementType`: [incompatible-type-arg]
2246-
- Either component NoRef [1] is incompatible with string [2].
2247-
- Or object type [3] is incompatible with string [4].
2248-
2249-
names.js:23:2
2250-
23| <NoRef /> // error again due to bad ref
2251-
^^^^^
2252-
2253-
References:
2254-
names.js:22:8
2255-
22| export component NoRef(ref: string) { return <div /> }; // Error
2256-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^ [1]
2257-
<BUILTINS>/react.js:131:5
2258-
131| | string
2259-
^^^^^^ [2]
2260-
<BUILTINS>/react.js:192:5
2261-
192| | { -current: T | null, ... }
2262-
^^^^^^^^^^^^^^^^^^^^^^^^^^^ [3]
2263-
names.js:22:29
2264-
22| export component NoRef(ref: string) { return <div /> }; // Error
2265-
^^^^^^ [4]
2266-
2267-
22682168
Error --------------------------------------------------------------------------------------------------- names.js:25:35
22692169

22702170
Components do not support ref properties [1] within spreads [2] [invalid-component-prop]
@@ -3807,7 +3707,7 @@ Strict mode function may not have duplicate parameter names
38073707

38083708

38093709

3810-
Found 243 errors
3710+
Found 239 errors
38113711

38123712
Only showing the most relevant union/intersection branches.
38133713
To see all branches, re-run Flow with --show-all-branches

tests/type_at_pos_react/type_at_pos_react.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ React$CreateElement
207207

208208
react.js:4:7,4:19
209209

210-
react_abstract_component.js:3:15 = component(ref: React.RefSetter<mixed>, ...any)
210+
react_abstract_component.js:3:15 = component(ref: empty, ...any)
211211
react_abstract_component.js:3:15,3:15
212212
react_abstract_component.js:8:15 = type RendersFoo = renders Foo
213213

0 commit comments

Comments
 (0)