Skip to content

Commit dd825da

Browse files
SamChou19815facebook-github-bot
authored andcommitted
[flow] Normalize React.AbstractComponent to the new component type, with best effort flattening
Summary: We want to one day kill `React.AbstractComponent`, since it's instance targ is not in-line with react 19's model of ref as a prop. People learn what types to write from hover, so it's important that we stop printing out these types. In this diff, we star to normalize React.AbstractComponent to the new component type, which is enabled regardless of whether component syntax is enabled. We have the following baseline strategy: 1. Props go into `component(...Props)` 2. Instance go into `component(ref: React.RefSetter<Instance>)` 3. Renders go into `component() renders Renders` For props, we also do some best effort flattening if the props have an object type that we can see in normalizer. For exact objects, it's straightforward; for inexact objects, we add `...{...}` at the end. We bail out on things like getters, methods, etc. For renders, we won't repeat the `render` keyword if the `Renders` already has a render type. Changelog: [ide] On hover, values that have `React.AbstractComponent` type will be shown in the [component type](https://flow.org/en/docs/react/component-types/) syntax. Reviewed By: jbrown215 Differential Revision: D62917950 fbshipit-source-id: 2464a9baf253219ed2dfb90e6ae9dc0bf6b79687
1 parent c0c9e46 commit dd825da

File tree

13 files changed

+319
-21
lines changed

13 files changed

+319
-21
lines changed

src/common/ty/ty.ml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,11 @@ type t =
7777
false_type: t;
7878
}
7979
| Infer of symbol * t option
80+
| Component of {
81+
props: component_props;
82+
instance: t option;
83+
renders: t;
84+
}
8085
| Renders of t * renders_kind
8186

8287
(* Recursive variable *)
@@ -294,6 +299,21 @@ and utility =
294299
| ReactCheckComponentRef of t
295300
| ReactConfigType of t * t
296301

302+
and component_props =
303+
| UnflattenedComponentProps of t
304+
| FlattenedComponentProps of {
305+
props: flattened_component_prop list;
306+
inexact: bool;
307+
}
308+
309+
and flattened_component_prop =
310+
| FlattenedComponentProp of {
311+
name: Reason.name;
312+
optional: bool;
313+
def_locs: aloc list;
314+
t: t;
315+
}
316+
297317
and renders_kind =
298318
| RendersNormal
299319
| RendersMaybe
@@ -580,7 +600,8 @@ class ['A] comparator_ty =
580600
| InlineInterface _ -> 26
581601
| Conditional _ -> 27
582602
| Infer _ -> 28
583-
| Renders _ -> 29
603+
| Component _ -> 29
604+
| Renders _ -> 30
584605

585606
method tag_of_decl _ =
586607
function
@@ -806,6 +827,7 @@ let mk_exact ty =
806827
| Tup _
807828
| InlineInterface _
808829
| Infer _
830+
| Component _
809831
| Renders _ ->
810832
ty
811833
(* Do not nest $Exact *)

src/common/ty/ty_debug.ml

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ let string_of_ctor_t = function
4343
| IndexedAccess _ -> "IndexedAccess"
4444
| Conditional _ -> "Conditional"
4545
| Infer _ -> "Infer"
46+
| Component _ -> "Component"
4647
| Renders _ -> "Renders"
4748

4849
let string_of_ctor_decl = function
@@ -387,6 +388,37 @@ struct
387388
"Infer (%s, %s)"
388389
(dump_symbol s)
389390
(Base.Option.value_map ~default:"None" ~f:(dump_t ~depth) b)
391+
| Component { props; instance; renders } ->
392+
let props =
393+
match props with
394+
| UnflattenedComponentProps t -> [spf "...%s" (dump_t ~depth t)]
395+
| FlattenedComponentProps { props; inexact } ->
396+
let props =
397+
Base.List.map
398+
props
399+
~f:(fun (FlattenedComponentProp { name; optional; def_locs = _; t }) ->
400+
spf
401+
"%s%s: %s"
402+
(Reason.display_string_of_name name)
403+
( if optional then
404+
"?"
405+
else
406+
""
407+
)
408+
(dump_t ~depth t)
409+
)
410+
in
411+
if inexact then
412+
props @ ["...{...}"]
413+
else
414+
props
415+
in
416+
let props =
417+
match instance with
418+
| None -> props
419+
| Some t -> spf "ref: React.RefSetter<%s>" (dump_t ~depth t) :: props
420+
in
421+
spf "Conditional(%s): %s" (Base.String.concat ~sep:", " props) (dump_t ~depth renders)
390422
| Renders (t, _) -> spf "Renders (%s)" (dump_t ~depth t)
391423

392424
and dump_class_decl ~depth (name, ps) =
@@ -561,6 +593,40 @@ struct
561593
("name", json_of_symbol s);
562594
("bound", Base.Option.value_map ~default:JSON_Null ~f:json_of_t b);
563595
]
596+
| Component { props; instance; renders } ->
597+
let props =
598+
match props with
599+
| UnflattenedComponentProps t ->
600+
JSON_Object [("kind", JSON_String "unflattened"); ("type", json_of_t t)]
601+
| FlattenedComponentProps { props; inexact } ->
602+
let props =
603+
Base.List.map
604+
props
605+
~f:(fun (FlattenedComponentProp { name; optional; def_locs; t }) ->
606+
JSON_Object
607+
[
608+
("name", JSON_String (Reason.display_string_of_name name));
609+
("optional", JSON_Bool optional);
610+
("type", json_of_t t);
611+
( "def_locs",
612+
JSON_Array
613+
(Base.List.map def_locs ~f:(fun loc -> JSON_String (string_of_aloc loc)))
614+
);
615+
]
616+
)
617+
in
618+
JSON_Object
619+
[
620+
("kind", JSON_String "flattened");
621+
("types", JSON_Array props);
622+
("inexact", JSON_Bool inexact);
623+
]
624+
in
625+
[
626+
("props", props);
627+
("instance", Base.Option.value_map instance ~f:json_of_t ~default:JSON_Null);
628+
("renders", json_of_t renders);
629+
]
564630
| Renders (t, variant) ->
565631
[
566632
("argument", json_of_t t);

src/common/ty/ty_printer.ml

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,66 @@ let layout_of_elt ~prefer_single_quotes ?(size = 5000) ?(with_comments = true) ~
201201
option ~f:(fun t -> fuse [space; Atom "extends"; space; type_ ~depth t]) b;
202202
];
203203
]
204+
| Component { props; instance; renders } ->
205+
let to_key x =
206+
if property_key_quotes_needed x then
207+
let quote = better_quote ~prefer_single_quotes x in
208+
fuse [Atom quote; Atom (utf8_escape ~quote x); Atom quote]
209+
else
210+
identifier (Reason.OrdinaryName x)
211+
in
212+
let params =
213+
match props with
214+
| UnflattenedComponentProps t -> [fuse [Atom "..."; type_ ~depth t]]
215+
| FlattenedComponentProps { props; inexact } ->
216+
let params =
217+
Base.List.map
218+
props
219+
~f:(fun (FlattenedComponentProp { name; optional; def_locs = _; t }) ->
220+
fuse
221+
[
222+
to_key (Reason.display_string_of_name name);
223+
( if optional then
224+
Atom "?"
225+
else
226+
Empty
227+
);
228+
Atom ":";
229+
pretty_space;
230+
type_ ~depth t;
231+
]
232+
)
233+
in
234+
if inexact then
235+
params @ [Atom "...{...}"]
236+
else
237+
params
238+
in
239+
let params =
240+
match instance with
241+
| None -> params
242+
| Some t ->
243+
fuse
244+
[
245+
Atom "ref:";
246+
pretty_space;
247+
Atom "React.RefSetter";
248+
list ~wrap:(Atom "<", Atom ">") ~sep:(Atom ",") [type_ ~depth t];
249+
]
250+
:: params
251+
in
252+
let renders =
253+
match renders with
254+
| Renders _ -> type_ ~depth renders
255+
| t -> fuse [Atom "renders"; space; type_with_parens ~depth t]
256+
in
257+
fuse
258+
[
259+
Atom "component";
260+
list ~wrap:(Atom "(", Atom ")") ~sep:(Atom ",") ~trailing:false params;
261+
space;
262+
renders;
263+
]
204264
| Renders (t, variant) ->
205265
let renders_str =
206266
match variant with

src/common/ty/ty_serializer.ml

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -182,15 +182,98 @@ let type_ options =
182182
| TypeOf (FunProto, _) -> just (qualified2 "Object" "prototype")
183183
| TypeOf (ObjProto, _) -> just (qualified2 "Function" "prototype")
184184
| TypeOf (FunProtoBind, _) -> just (qualified3 "Function" "prototype" "bind")
185-
| Renders (t, kind) ->
186-
let argument = type_ t in
187-
let variant =
188-
match kind with
189-
| RendersNormal -> T.Renders.Normal
190-
| RendersMaybe -> T.Renders.Maybe
191-
| RendersStar -> T.Renders.Star
185+
| Component { props; instance; renders = renders_ } ->
186+
let all_params =
187+
match props with
188+
| UnflattenedComponentProps t ->
189+
let rest_param =
190+
{
191+
T.Component.RestParam.argument = None;
192+
annot = type_ t;
193+
optional = false;
194+
comments = None;
195+
}
196+
in
197+
{ T.Component.Params.params = []; rest = Some (just rest_param); comments = None }
198+
| FlattenedComponentProps { props; inexact } ->
199+
let params =
200+
Base.List.map
201+
props
202+
~f:(fun (FlattenedComponentProp { name; optional; def_locs = _; t }) ->
203+
let name =
204+
let x = Reason.show_name name in
205+
if Ty_printer.property_key_quotes_needed x then
206+
let quote = Ty_printer.better_quote ~prefer_single_quotes:false x in
207+
let raw = quote ^ Ty_printer.utf8_escape ~quote x ^ quote in
208+
Ast.Statement.ComponentDeclaration.Param.StringLiteral
209+
(Loc.none, { Ast.StringLiteral.value = x; raw; comments = None })
210+
else
211+
Ast.Statement.ComponentDeclaration.Param.Identifier (id_from_string x)
212+
in
213+
let annot = annotation t in
214+
just { T.Component.Param.name; optional; annot }
215+
)
216+
in
217+
let rest =
218+
if inexact then
219+
Some
220+
(just
221+
{
222+
T.Component.RestParam.argument = None;
223+
annot =
224+
just
225+
(T.Object
226+
{
227+
T.Object.exact = false;
228+
inexact = true;
229+
properties = [];
230+
comments = None;
231+
}
232+
);
233+
optional = false;
234+
comments = None;
235+
}
236+
)
237+
else
238+
None
239+
in
240+
{ T.Component.Params.params; rest; comments = None }
192241
in
193-
just (T.Renders { T.Renders.operator_loc = Loc.none; argument; comments = None; variant })
242+
let all_params =
243+
match instance with
244+
| None -> all_params
245+
| Some t ->
246+
let ref_prop =
247+
just
248+
{
249+
T.Component.Param.name =
250+
Ast.Statement.ComponentDeclaration.Param.Identifier (id_from_string "ref");
251+
optional = false;
252+
annot =
253+
just
254+
(mk_generic_type (id_from_string "React.RefSetter") (Some (mk_targs [type_ t])));
255+
}
256+
in
257+
let params = ref_prop :: all_params.T.Component.Params.params in
258+
{ all_params with T.Component.Params.params }
259+
in
260+
let params = just all_params in
261+
let renders =
262+
match renders_ with
263+
| Renders (t, kind) -> T.AvailableRenders (Loc.none, renders t kind)
264+
| _ -> T.AvailableRenders (Loc.none, renders t RendersNormal)
265+
in
266+
just (T.Component { T.Component.tparams = None; params; renders; comments = None })
267+
| Renders (t, kind) -> just (T.Renders (renders t kind))
268+
and renders t kind =
269+
let argument = type_ t in
270+
let variant =
271+
match kind with
272+
| RendersNormal -> T.Renders.Normal
273+
| RendersMaybe -> T.Renders.Maybe
274+
| RendersStar -> T.Renders.Star
275+
in
276+
{ T.Renders.operator_loc = Loc.none; argument; comments = None; variant }
194277
and generic x targs =
195278
let id = id_from_symbol x in
196279
let targs = Base.Option.map ~f:type_arguments targs in

src/services/autocomplete/autocompleteService_js.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ let lsp_completion_of_type =
129129
| Utility _
130130
| IndexedAccess _
131131
| Conditional _
132+
| Component _
132133
| Infer _
133134
| Renders _ ->
134135
Lsp.Completion.Variable

src/typing/ty_members.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@ let rec members_of_ty : Ty.t -> Ty.t member_info NameUtils.Map.t * string list =
302302
| Utility _
303303
| IndexedAccess _
304304
| Conditional _
305+
| Component _
305306
| Infer _
306307
| Renders _ ->
307308
(NameUtils.Map.empty, [])

src/typing/ty_normalizer.ml

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -783,15 +783,35 @@ module Make (I : INPUT) : S = struct
783783
let%bind config = type__ ~env config in
784784
let%bind instance =
785785
match instance with
786-
| ComponentInstanceOmitted _ -> return Ty.Void
787-
| ComponentInstanceAvailable t -> type__ ~env t
786+
| ComponentInstanceOmitted _ -> return None
787+
| ComponentInstanceAvailable t -> type__ ~env t >>| Base.Option.some
788788
in
789789
let%bind renders = type__ ~env renders in
790-
return
791-
(generic_talias
792-
(Ty_symbol.builtin_symbol (Reason.OrdinaryName "React$AbstractComponent"))
793-
(Some [config; instance; renders])
794-
)
790+
let props =
791+
let props_flattened =
792+
match config with
793+
| Ty.Obj
794+
{
795+
Ty.obj_def_loc = _;
796+
obj_literal = Some false | None;
797+
obj_props;
798+
obj_kind = (Ty.ExactObj | Ty.InexactObj) as obj_kind;
799+
} ->
800+
Base.List.fold_result obj_props ~init:[] ~f:(fun acc -> function
801+
| Ty.NamedProp { name; prop = Ty.Field { t; polarity = _; optional }; def_locs; _ }
802+
->
803+
let prop = Ty.FlattenedComponentProp { name; optional; def_locs; t } in
804+
Ok (prop :: acc)
805+
| _ -> Error ()
806+
)
807+
|> Base.Result.map ~f:(fun props -> (props, obj_kind = Ty.InexactObj))
808+
| _ -> Error ()
809+
in
810+
match props_flattened with
811+
| Ok (props, inexact) -> Ty.FlattenedComponentProps { props = List.rev props; inexact }
812+
| Error () -> Ty.UnflattenedComponentProps config
813+
in
814+
return (Ty.Component { props; instance; renders })
795815
| DefT (_, RendersT (InstrinsicRenders n)) -> return (Ty.StrLit (OrdinaryName n))
796816
| DefT (r, RendersT (NominalRenders { renders_id = _; renders_name; _ })) ->
797817
let symbol =

tests/autocomplete/autocomplete.exp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5723,7 +5723,10 @@ Flags: --pretty
57235723
{
57245724
"result":[
57255725
{"name":"ab","type":"number"},
5726-
{"name":"ac","type":"React$AbstractComponent<Props, any, React$Node>"}
5726+
{
5727+
"name":"ac",
5728+
"type":"component(ref: React.RefSetter<any>, ...Props) renders React$Node"
5729+
}
57275730
]
57285731
}
57295732

tests/autocomplete_react/autocomplete_react.exp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ Flags: --pretty
387387
"result":[
388388
{
389389
"name":"AbstractComponent",
390-
"type":"type AbstractComponent<-Config, +Instance = mixed, +Renders: React$Node = React$Node> = React$AbstractComponent<Config, Instance, Renders>"
390+
"type":"type AbstractComponent<-Config, +Instance = mixed, +Renders: React$Node = React$Node> = component(ref: React.RefSetter<Instance>, ...Config) renders Renders"
391391
},
392392
{
393393
"name":"ChildrenArray",

0 commit comments

Comments
 (0)