Skip to content

Commit

Permalink
Add $Call utility type
Browse files Browse the repository at this point in the history
Reviewed By: samwgoldman

Differential Revision: D5722285

fbshipit-source-id: 72c9377102e74b53099573c43693d789b7cccef6
  • Loading branch information
Caleb Meredith authored and facebook-github-bot committed Sep 6, 2017
1 parent bd938ab commit ac7d9ac
Show file tree
Hide file tree
Showing 11 changed files with 309 additions and 7 deletions.
19 changes: 12 additions & 7 deletions src/typing/debug_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -1085,25 +1085,28 @@ and json_of_destructor_impl json_cx = Hh_json.(function
| ValuesType -> JSON_Object [
"values", JSON_Bool true;
]
| CallType args -> JSON_Object [
"args", JSON_Array (List.map (_json_of_t json_cx) args);
]
| TypeMap tmap -> json_of_type_map json_cx tmap
| ReactElementPropsType -> JSON_Object [
"reactElementProps", JSON_Bool true
]
"reactElementProps", JSON_Bool true
]
| ReactElementRefType -> JSON_Object [
"reactElementRef", JSON_Bool true
]
"reactElementRef", JSON_Bool true
]
)

and json_of_type_map json_cx = check_depth json_of_type_map_impl json_cx
and json_of_type_map_impl json_cx = Hh_json.(function
| TupleMap t -> JSON_Object [
"tupleMap", _json_of_t json_cx t
"tupleMap", _json_of_t json_cx t;
]
| ObjectMap t -> JSON_Object [
"objectMap", _json_of_t json_cx t
"objectMap", _json_of_t json_cx t;
]
| ObjectMapi t -> JSON_Object [
"objectMapi", _json_of_t json_cx t
"objectMapi", _json_of_t json_cx t;
]
)

Expand Down Expand Up @@ -1532,6 +1535,7 @@ and dump_t_ (depth, tvars) cx t =
| Bind _ -> "bind"
| SpreadType _ -> "spread"
| ValuesType -> "values"
| CallType _ -> "function call"
| TypeMap (TupleMap _) -> "tuple map"
| TypeMap (ObjectMap _) -> "object map"
| TypeMap (ObjectMapi _) -> "object mapi"
Expand Down Expand Up @@ -2155,6 +2159,7 @@ let string_of_destructor = function
| Bind _ -> "Bind"
| SpreadType _ -> "Spread"
| ValuesType -> "Values"
| CallType _ -> "CallType"
| TypeMap (TupleMap _) -> "TupleMap"
| TypeMap (ObjectMap _) -> "ObjectMap"
| TypeMap (ObjectMapi _) -> "ObjectMapi"
Expand Down
4 changes: 4 additions & 0 deletions src/typing/flow_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -6717,6 +6717,10 @@ and eval_destructor cx ~trace reason curr_t s i =
let state = { todo_rev; acc = [] } in
ObjSpreadT (reason, options, tool, state, tvar)
| ValuesType -> GetValuesT (reason, tvar)
| CallType args ->
let call = mk_functioncalltype (List.map (fun arg -> Arg arg) args) tvar in
let call = {call with call_strict_arity = false} in
CallT (reason, call)
| TypeMap tmap -> MapTypeT (reason, tmap, tvar)
| ReactElementPropsType -> ReactKitT (reason, React.GetProps tvar)
| ReactElementRefType -> ReactKitT (reason, React.GetRef tvar)
Expand Down
1 change: 1 addition & 0 deletions src/typing/gc_js.ml
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,7 @@ and gc_destructor cx state = function
| ElementType t -> gc cx state t
| Bind t -> gc cx state t
| SpreadType (_, ts) -> List.iter (gc cx state) ts
| CallType args -> List.iter (gc cx state) args
| TypeMap tmap -> gc_type_map cx state tmap

and gc_pred cx state = function
Expand Down
1 change: 1 addition & 0 deletions src/typing/type.ml
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ module rec TypeTerm : sig
| Bind of t
| SpreadType of ObjectSpread.options * t list
| ValuesType
| CallType of t list
| TypeMap of type_map
| ReactElementPropsType
| ReactElementRefType
Expand Down
8 changes: 8 additions & 0 deletions src/typing/type_annotation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,14 @@ let rec convert cx tparams_map = Ast.Type.(function
AbstractT (reason, t)
)

| "$Call" ->
(match convert_type_params () with
| fn::args ->
let reason = mk_reason RFunctionCall loc in
EvalT (fn, TypeDestructorT (reason, CallType args), mk_id ())
| _ ->
error_type cx loc (FlowError.ETypeParamMinArity (loc, 1)))

| "$TupleMap" ->
check_type_param_arity cx loc typeParameters 2 (fun () ->
let t1, t2 = match convert_type_params () with
Expand Down
4 changes: 4 additions & 0 deletions src/typing/type_mapper.ml
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,10 @@ class ['a] t = object(self)
if tlist' == tlist then t
else SpreadType (options, tlist')
| ValuesType -> t
| CallType args ->
let args' = ListUtils.ident_map (self#type_ cx map_cx) args in
if args' == args then t
else CallType args'
| TypeMap tmap ->
let tmap' = self#type_map cx map_cx tmap in
if tmap' == tmap then t
Expand Down
1 change: 1 addition & 0 deletions src/typing/type_visitor.ml
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ class ['a] t = object(self)
| ElementType t -> self#type_ cx acc t
| Bind t -> self#type_ cx acc t
| SpreadType (_, ts) -> self#list (self#type_ cx) acc ts
| CallType args -> self#list (self#type_ cx) acc args
| TypeMap (TupleMap t | ObjectMap t | ObjectMapi t) -> self#type_ cx acc t

method private custom_fun_kind cx acc = function
Expand Down
Empty file added tests/call_type/.flowconfig
Empty file.
212 changes: 212 additions & 0 deletions tests/call_type/call_type.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
Error: test.js:7
7: type A = $Call; // Error: one or more arguments are required.
^^^^^ Incorrect number of type parameters (expected at least 1)

Error: test.js:8
8: type B = $Call<>; // Error: one or more arguments are required.
^^^^^^^ Incorrect number of type parameters (expected at least 1)

Error: test.js:13
13: ((null: mixed): C); // Error: mixed ~> number
^^^^^ mixed. This type is incompatible with
13: ((null: mixed): C); // Error: mixed ~> number
^ number

Error: test.js:15
15: (c: empty); // Error: number ~> empty
^ number. This type is incompatible with
15: (c: empty); // Error: number ~> empty
^^^^^ empty

Error: test.js:19
19: (42: D); // Error: function called with to few arguments.
^^ number. This type is incompatible with
19: (42: D); // Error: function called with to few arguments.
^ undefined (too few arguments, expected default/rest parameters)

Error: test.js:24
24: ((null: mixed): E); // Error: mixed ~> number
^^^^^ mixed. This type is incompatible with
24: ((null: mixed): E); // Error: mixed ~> number
^ number

Error: test.js:26
26: (e: empty); // Error: number ~> empty
^ number. This type is incompatible with
26: (e: empty); // Error: number ~> empty
^^^^^ empty

Error: test.js:31
31: ((null: mixed): F); // Error: mixed ~> number
^^^^^ mixed. This type is incompatible with
31: ((null: mixed): F); // Error: mixed ~> number
^ number

Error: test.js:33
33: (f: empty); // Error: number ~> empty
^ number. This type is incompatible with
33: (f: empty); // Error: number ~> empty
^^^^^ empty

Error: test.js:38
38: ((null: mixed): G); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
38: ((null: mixed): G); // Error: mixed ~> number | string
^ union: type parameter `A` of function call | type parameter `B` of function call
Member 1:
35: type G = $Call<Fn2, number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `A` of function call
Error:
38: ((null: mixed): G); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
35: type G = $Call<Fn2, number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^ number
Member 2:
35: type G = $Call<Fn2, number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `B` of function call
Error:
38: ((null: mixed): G); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
35: type G = $Call<Fn2, number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^ string

Error: test.js:39
39: (g: number); // Error: number | string ~> number
^ string. This type is incompatible with
39: (g: number); // Error: number | string ~> number
^^^^^^ number

Error: test.js:41
41: (g: empty); // Error: number | string ~> empty
^ number. This type is incompatible with
41: (g: empty); // Error: number | string ~> empty
^^^^^ empty

Error: test.js:41
41: (g: empty); // Error: number | string ~> empty
^ string. This type is incompatible with
41: (g: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:6
6: ((null: mixed): A); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
6: ((null: mixed): A); // Error: mixed ~> number | string
^ union: function call(s)
Member 1:
3: type A = $Call<(() => number) | (() => string)>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call
Error:
6: ((null: mixed): A); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
3: type A = $Call<(() => number) | (() => string)>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ number
Member 2:
3: type A = $Call<(() => number) | (() => string)>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call
Error:
6: ((null: mixed): A); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
3: type A = $Call<(() => number) | (() => string)>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string

Error: union.js:7
7: (a: number); // Error: number | string ~> number
^ string. This type is incompatible with
7: (a: number); // Error: number | string ~> number
^^^^^^ number

Error: union.js:9
9: (a: empty); // Error: number | string ~> empty
^ number. This type is incompatible with
9: (a: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:9
9: (a: empty); // Error: number | string ~> empty
^ string. This type is incompatible with
9: (a: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:14
14: ((null: mixed): B); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
14: ((null: mixed): B); // Error: mixed ~> number | string
^ union: function call(s)
Member 1:
11: type B = $Call<(<T>(T) => T) | (<T>(any, T) => T), number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call
Error:
14: ((null: mixed): B); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
11: type B = $Call<(<T>(T) => T) | (<T>(any, T) => T), number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ number
Member 2:
11: type B = $Call<(<T>(T) => T) | (<T>(any, T) => T), number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function call
Error:
14: ((null: mixed): B); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
11: type B = $Call<(<T>(T) => T) | (<T>(any, T) => T), number, string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string

Error: union.js:15
15: (b: number); // Error: number | string ~> number
^ string. This type is incompatible with
15: (b: number); // Error: number | string ~> number
^^^^^^ number

Error: union.js:17
17: (b: empty); // Error: number | string ~> empty
^ number. This type is incompatible with
17: (b: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:17
17: (b: empty); // Error: number | string ~> empty
^ string. This type is incompatible with
17: (b: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:22
22: ((null: mixed): C); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
22: ((null: mixed): C); // Error: mixed ~> number | string
^ union: number | string
Member 1:
19: type C = $Call<<T>(T) => T, number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ number
Error:
22: ((null: mixed): C); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
19: type C = $Call<<T>(T) => T, number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ number
Member 2:
19: type C = $Call<<T>(T) => T, number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string
Error:
22: ((null: mixed): C); // Error: mixed ~> number | string
^^^^^ mixed. This type is incompatible with
19: type C = $Call<<T>(T) => T, number | string>;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ string

Error: union.js:23
23: (c: number); // Error: number | string ~> number
^ string. This type is incompatible with
23: (c: number); // Error: number | string ~> number
^^^^^^ number

Error: union.js:25
25: (c: empty); // Error: number | string ~> empty
^ number. This type is incompatible with
25: (c: empty); // Error: number | string ~> empty
^^^^^ empty

Error: union.js:25
25: (c: empty); // Error: number | string ~> empty
^ string. This type is incompatible with
25: (c: empty); // Error: number | string ~> empty
^^^^^ empty


Found 25 errors
41 changes: 41 additions & 0 deletions tests/call_type/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @flow

type Fn0 = () => number;
type Fn1 = <T>(T) => T;
type Fn2 = <A, B>(A, B) => A | B;

type A = $Call; // Error: one or more arguments are required.
type B = $Call<>; // Error: one or more arguments are required.

type C = $Call<Fn0>;
declare var c: C;
(42: C); // OK
((null: mixed): C); // Error: mixed ~> number
(c: number); // OK
(c: empty); // Error: number ~> empty

type D = $Call<Fn1>;
declare var d: D;
(42: D); // Error: function called with to few arguments.

type E = $Call<Fn1, number>;
declare var e: E;
(42: E); // OK
((null: mixed): E); // Error: mixed ~> number
(e: number); // OK
(e: empty); // Error: number ~> empty

type F = $Call<Fn1, number, string>;
declare var f: F;
(42: F); // OK
((null: mixed): F); // Error: mixed ~> number
(f: number); // OK
(f: empty); // Error: number ~> empty

type G = $Call<Fn2, number, string>;
declare var g: G;
(42: G); // OK
((null: mixed): G); // Error: mixed ~> number | string
(g: number); // Error: number | string ~> number
(g: number | string); // OK
(g: empty); // Error: number | string ~> empty
25 changes: 25 additions & 0 deletions tests/call_type/union.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// @flow

type A = $Call<(() => number) | (() => string)>;
declare var a: A;
(42: A); // OK
((null: mixed): A); // Error: mixed ~> number | string
(a: number); // Error: number | string ~> number
(a: number | string); // OK
(a: empty); // Error: number | string ~> empty

type B = $Call<(<T>(T) => T) | (<T>(any, T) => T), number, string>;
declare var b: B;
(42: B); // OK
((null: mixed): B); // Error: mixed ~> number | string
(b: number); // Error: number | string ~> number
(b: number | string); // OK
(b: empty); // Error: number | string ~> empty

type C = $Call<<T>(T) => T, number | string>;
declare var c: C;
(42: C); // OK
((null: mixed): C); // Error: mixed ~> number | string
(c: number); // Error: number | string ~> number
(c: number | string); // OK
(c: empty); // Error: number | string ~> empty

1 comment on commit ac7d9ac

@jcready
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation updates?

Please sign in to comment.