Skip to content

Commit 682e566

Browse files
gkzfacebook-github-bot
authored andcommitted
[flow][tuples] Implement tuple type spread reversal
Summary: Changelog: [feature] You can have a tuple spread of a generic in the parameter of a function and not have to supply the relevant type argument if that spread is the only spread in that tuple, and it is the last element of that tuple. Reviewed By: SamChou19815 Differential Revision: D55721533 fbshipit-source-id: a8fbd9e09fd40839e8e7bff0fd02b99dbe1ab801
1 parent 2fd9e51 commit 682e566

File tree

4 files changed

+133
-18
lines changed

4 files changed

+133
-18
lines changed

src/typing/flow_js_utils.ml

+1-1
Original file line numberDiff line numberDiff line change
@@ -2685,7 +2685,7 @@ let mk_tuple_type cx ?trace ~id ~mk_type_destructor reason elements =
26852685
match (el, first_spread) with
26862686
| (UnresolvedArg (el, generic), None) ->
26872687
((el, generic) :: resolved, unresolved, first_spread)
2688-
| (UnresolvedSpreadArg t, None) -> (resolved, unresolved, Some (reason, t))
2688+
| (UnresolvedSpreadArg t, None) -> (resolved, unresolved, Some (reason_of_t t, t))
26892689
| (_, Some _) -> (resolved, el :: unresolved, first_spread)
26902690
)
26912691
in

src/typing/implicit_instantiation.ml

+21-1
Original file line numberDiff line numberDiff line change
@@ -278,7 +278,27 @@ module Make (Observer : OBSERVER) (Flow : Flow_common.S) : S = struct
278278
|> merge_lower_bounds cx
279279
|> use_t_result_of_t_option
280280
)
281-
| SpreadTupleType _ -> UpperNonT u)
281+
| SpreadTupleType { reason_tuple = _; reason_spread; resolved; unresolved } ->
282+
let is_spread = function
283+
| ResolvedArg _ -> false
284+
| ResolvedSpreadArg _
285+
| ResolvedAnySpreadArg _ ->
286+
true
287+
in
288+
(* Reverse if the spread is the last item in the tuple and there are
289+
no other spreads. *)
290+
(match (unresolved, Base.List.exists ~f:is_spread resolved) with
291+
| ([], false) ->
292+
let n = List.length resolved in
293+
merge_lower_or_upper_bounds r (OpenT tout)
294+
|> bind_use_t_result ~f:(fun t ->
295+
Tvar.mk_where cx reason_spread (fun t' ->
296+
Flow.flow cx (t, ArrRestT (unknown_use, reason_spread, n, t'))
297+
)
298+
|> merge_lower_bounds cx
299+
|> use_t_result_of_t_option
300+
)
301+
| _ -> UpperNonT u))
282302
| UseT (_, t) -> UpperT t
283303
| ArrRestT (_, _, i, tout) ->
284304
(match get_t cx tout with

tests/tuples/spread.js

+26-3
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,29 @@ type WithTail<T> = [1, ...T];
8686
[1, 2, 3] as WithTail<[2, 3]>; // OK
8787
[1, 2, 66] as WithTail<[2, 3]>; // ERROR
8888

89-
declare function f<T>(xs: [1, ...T]): void;
90-
f([1, 2, 3]); // ERROR: annotation required - reversal not yet implemented
91-
f<[2, 3]>([ 1, 2, 3]); // OK
89+
// Tuple-like array spread
90+
{
91+
const x = [1, 2];
92+
type TupleArrSpread = [0, ...typeof x]; // OK
93+
[0, 1, 2] as TupleArrSpread; // OK
94+
}
95+
96+
declare function tail<T>(xs: [1, ...T]): T;
97+
tail<[2, 3]>([ 1, 2, 3]); // OK
98+
{
99+
const x = tail([1]); // OK
100+
x as []; // OK
101+
}
102+
{
103+
const x = tail([1, 2, 3]);
104+
x as [2, 3]; // OK
105+
x as [1, 2, 3]; // ERROR
106+
}
107+
tail([666, 2, 3]); // ERROR
108+
tail([]); // ERROR
109+
110+
declare function noReversal1<T>(xs: [1, ...T, 2]): T;
111+
noReversal1([1, 9, 2]); // ERROR
112+
113+
declare function noReversal2<T>(xs: [...T, ...any]): T;
114+
noReversal2([1, 2, 3]); // ERROR

tests/tuples/tuples.exp

+85-13
Original file line numberDiff line numberDiff line change
@@ -1211,22 +1211,94 @@ References:
12111211
^ [2]
12121212

12131213

1214-
Error --------------------------------------------------------------------------------------------------- spread.js:90:1
1214+
Error --------------------------------------------------------------------------------------------------- spread.js:92:8
12151215

1216-
Cannot call `f` because `T` [1] is underconstrained by call of `f` [2]. Either add explicit type arguments or cast the
1217-
expression to your expected type. [underconstrained-implicit-instantiation]
1216+
Cannot declare `TupleArrSpread` [1] because the name is already bound. [name-already-bound]
12181217

1219-
spread.js:90:1
1220-
90| f([1, 2, 3]); // ERROR: annotation required - reversal not yet implemented
1221-
^
1218+
spread.js:92:8
1219+
92| type TupleArrSpread = [0, ...typeof x]; // OK
1220+
^^^^^^^^^^^^^^
12221221

12231222
References:
1224-
spread.js:89:20
1225-
89| declare function f<T>(xs: [1, ...T]): void;
1226-
^ [1]
1227-
spread.js:90:1
1228-
90| f([1, 2, 3]); // ERROR: annotation required - reversal not yet implemented
1229-
^^^^^^^^^^^^ [2]
1223+
spread.js:80:8
1224+
80| type TupleArrSpread = [0, ...typeof x]; // OK
1225+
^^^^^^^^^^^^^^ [1]
1226+
1227+
1228+
Error -------------------------------------------------------------------------------------------------- spread.js:105:3
1229+
1230+
Cannot cast `x` to tuple type because `T` [1] has 2 elements but tuple type [2] has 3 elements. [invalid-tuple-arity]
1231+
1232+
spread.js:105:3
1233+
105| x as [1, 2, 3]; // ERROR
1234+
^
1235+
1236+
References:
1237+
spread.js:96:37
1238+
96| declare function tail<T>(xs: [1, ...T]): T;
1239+
^ [1]
1240+
spread.js:105:8
1241+
105| x as [1, 2, 3]; // ERROR
1242+
^^^^^^^^^ [2]
1243+
1244+
1245+
Error -------------------------------------------------------------------------------------------------- spread.js:107:7
1246+
1247+
Cannot call `tail` with array literal bound to `xs` because number [1] is incompatible with number literal `1` [2] in
1248+
index 0. [incompatible-call]
1249+
1250+
spread.js:107:7
1251+
107| tail([666, 2, 3]); // ERROR
1252+
^^^ [1]
1253+
1254+
References:
1255+
spread.js:96:31
1256+
96| declare function tail<T>(xs: [1, ...T]): T;
1257+
^ [2]
1258+
1259+
1260+
Error -------------------------------------------------------------------------------------------------- spread.js:108:6
1261+
1262+
Cannot determine type of empty array literal. Please provide an annotation. [missing-empty-array-annot]
1263+
1264+
108| tail([]); // ERROR
1265+
^^
1266+
1267+
1268+
Error -------------------------------------------------------------------------------------------------- spread.js:111:1
1269+
1270+
Cannot call `noReversal1` because `T` [1] is underconstrained by call of `noReversal1` [2]. Either add explicit type
1271+
arguments or cast the expression to your expected type. [underconstrained-implicit-instantiation]
1272+
1273+
spread.js:111:1
1274+
111| noReversal1([1, 9, 2]); // ERROR
1275+
^^^^^^^^^^^
1276+
1277+
References:
1278+
spread.js:110:30
1279+
110| declare function noReversal1<T>(xs: [1, ...T, 2]): T;
1280+
^ [1]
1281+
spread.js:111:1
1282+
111| noReversal1([1, 9, 2]); // ERROR
1283+
^^^^^^^^^^^^^^^^^^^^^^ [2]
1284+
1285+
1286+
Error -------------------------------------------------------------------------------------------------- spread.js:114:1
1287+
1288+
Cannot call `noReversal2` because `T` [1] is underconstrained by call of `noReversal2` [2]. Either add explicit type
1289+
arguments or cast the expression to your expected type. [underconstrained-implicit-instantiation]
1290+
1291+
spread.js:114:1
1292+
114| noReversal2([1, 2, 3]); // ERROR
1293+
^^^^^^^^^^^
1294+
1295+
References:
1296+
spread.js:113:30
1297+
113| declare function noReversal2<T>(xs: [...T, ...any]): T;
1298+
^ [1]
1299+
spread.js:114:1
1300+
114| noReversal2([1, 2, 3]); // ERROR
1301+
^^^^^^^^^^^^^^^^^^^^^^ [2]
12301302

12311303

12321304
Error --------------------------------------------------------------------------------------------------- too-few.js:5:5
@@ -1998,4 +2070,4 @@ References:
19982070

19992071

20002072

2001-
Found 125 errors
2073+
Found 130 errors

0 commit comments

Comments
 (0)