Skip to content

Commit c710c40

Browse files
Basil HosmerFacebook Github Bot 6
authored andcommitted
Sugar for exact object types
Summary: Adds support for `{| ... |}` exact object type declaration syntax. Given an object type `{ a: X, b: Y, ... }`, `{| a: X, b: Y, ... |}` is synonymous with `$Exact<{ a: X, b: Y, ... }>`. Note that `$Exact<X>` is still the only way to express the exact version of an aliased object type `X`. Reviewed By: avikchaudhuri Differential Revision: D3714440 fbshipit-source-id: df4ffa43a35397445880f6315c42b44edf60a527
1 parent b11bbbc commit c710c40

10 files changed

Lines changed: 70 additions & 36 deletions

newtests/exact/prop_test2.js

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,29 @@
11
/***
2-
* more prop tests on unions of objects.
3-
* to see why property existence tests don't work to refine inexact types,
4-
* take a look at checkFlag_err() below.
2+
* more prop tests on unions of objects,
3+
* using {| ... |} annotation syntax
54
*/
65

7-
type StringFlag = {
6+
type Flag = {|
87
type: "string",
98
name: string,
109
description: string,
1110
argName: string,
1211
aliases?: Array<string>,
1312
default?: string,
14-
};
15-
16-
type BoolFlag = {
13+
|} | {|
1714
type: "boolean",
1815
name: string,
1916
description: string,
2017
aliases?: Array<string>,
21-
};
22-
23-
type EnumFlag = {
18+
|} | {|
2419
type: "enum",
2520
name: string,
2621
description: string,
2722
argName: string,
2823
validValues: Array<string>,
2924
aliases?: Array<string>,
3025
default?: string,
31-
};
32-
33-
type Flag = $Exact<StringFlag> | $Exact<BoolFlag> | $Exact<EnumFlag>;
26+
|};
3427

3528
function checkFlag_ok(flag: Flag): string {
3629
if (flag.default) {

src/parser/estree_translator.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,7 @@ end with type t = Impl.t) = struct
940940

941941
and object_type (loc, o) = Type.Object.(
942942
node "ObjectTypeAnnotation" loc [|
943+
"exact", bool o.exact;
943944
"properties", array_of_list object_type_property o.properties;
944945
"indexers", array_of_list object_type_indexer o.indexers;
945946
"callProperties", array_of_list object_type_call_property o.callProperties;

src/parser/lexer_flow.mll

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ module Token = struct
2121
(* Syntax *)
2222
| T_LCURLY
2323
| T_RCURLY
24+
| T_LCURLYBAR
25+
| T_RCURLYBAR
2426
| T_LPAREN
2527
| T_RPAREN
2628
| T_LBRACKET
@@ -211,6 +213,8 @@ module Token = struct
211213
| T_AWAIT -> "T_AWAIT"
212214
| T_LCURLY -> "T_LCURLY"
213215
| T_RCURLY -> "T_RCURLY"
216+
| T_LCURLYBAR -> "T_LCURLYBAR"
217+
| T_RCURLYBAR -> "T_RCURLYBAR"
214218
| T_LPAREN -> "T_LPAREN"
215219
| T_RPAREN -> "T_RPAREN"
216220
| T_LBRACKET -> "T_LBRACKET"
@@ -1091,6 +1095,8 @@ and type_token env = parse
10911095
| "]" { env, T_RBRACKET }
10921096
| "{" { env, T_LCURLY }
10931097
| "}" { env, T_RCURLY }
1098+
| "{|" { env, T_LCURLYBAR }
1099+
| "|}" { env, T_RCURLYBAR }
10941100
| "(" { env, T_LPAREN }
10951101
| ")" { env, T_RPAREN }
10961102
| "..." { env, T_ELLIPSIS }

src/parser/parser_flow.ml

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -201,9 +201,10 @@ end = struct
201201
loc, Type.Exists
202202
| T_LESS_THAN -> _function env
203203
| T_LPAREN -> function_or_group env
204-
| T_LCURLY ->
205-
let loc, o = _object env in
206-
loc, Type.Object o
204+
| T_LCURLY
205+
| T_LCURLYBAR ->
206+
let loc, o = _object env ~allow_exact:true in
207+
loc, Type.Object o
207208
| T_TYPEOF ->
208209
let start_loc = Peek.loc env in
209210
Expect.token env T_TYPEOF;
@@ -465,27 +466,35 @@ end = struct
465466
static;
466467
})
467468

468-
in let semicolon env =
469+
in let semicolon exact env =
469470
match Peek.token env with
470471
| T_COMMA | T_SEMICOLON -> Eat.token env
471-
| T_RCURLY -> ()
472+
| T_RCURLYBAR when exact -> ()
473+
| T_RCURLY when not exact -> ()
472474
| _ -> error_unexpected env
473475

474-
in let rec properties ~allow_static env (acc, indexers, callProperties) =
476+
in let rec properties ~allow_static ~exact env
477+
(acc, indexers, callProperties) =
475478
let start_loc = Peek.loc env in
476479
let static = allow_static && Expect.maybe env T_STATIC in
477480
match Peek.token env with
478-
| T_EOF
479-
| T_RCURLY -> List.rev acc, List.rev indexers, List.rev callProperties
481+
| T_EOF ->
482+
List.rev acc, List.rev indexers, List.rev callProperties
483+
| T_RCURLYBAR when exact ->
484+
List.rev acc, List.rev indexers, List.rev callProperties
485+
| T_RCURLY when not exact ->
486+
List.rev acc, List.rev indexers, List.rev callProperties
480487
| T_LBRACKET ->
481488
let indexer = indexer_property env start_loc static in
482-
semicolon env;
483-
properties allow_static env (acc, indexer::indexers, callProperties)
489+
semicolon exact env;
490+
properties allow_static exact env
491+
(acc, indexer::indexers, callProperties)
484492
| T_LESS_THAN
485493
| T_LPAREN ->
486494
let call_prop = call_property env start_loc static in
487-
semicolon env;
488-
properties allow_static env (acc, indexers, call_prop::callProperties)
495+
semicolon exact env;
496+
properties allow_static exact env
497+
(acc, indexers, call_prop::callProperties)
489498
| _ ->
490499
let static, (_, key) = match static, Peek.token env with
491500
| true, T_COLON ->
@@ -507,17 +516,20 @@ end = struct
507516
| T_LESS_THAN
508517
| T_LPAREN -> method_property env start_loc static key
509518
| _ -> property env start_loc static key in
510-
semicolon env;
511-
properties allow_static env (property::acc, indexers, callProperties)
519+
semicolon exact env;
520+
properties allow_static exact env
521+
(property::acc, indexers, callProperties)
512522

513-
in fun ?(allow_static=false) env ->
523+
in fun ?(allow_static=false) ?(allow_exact=false) env ->
524+
let exact = allow_exact && Peek.token env = T_LCURLYBAR in
514525
let start_loc = Peek.loc env in
515-
Expect.token env T_LCURLY;
526+
Expect.token env (if exact then T_LCURLYBAR else T_LCURLY);
516527
let properties, indexers, callProperties =
517-
properties ~allow_static env ([], [], []) in
528+
properties ~allow_static ~exact env ([], [], []) in
518529
let end_loc = Peek.loc env in
519-
Expect.token env T_RCURLY;
530+
Expect.token env (if exact then T_RCURLYBAR else T_RCURLY);
520531
Loc.btwn start_loc end_loc, Type.Object.({
532+
exact;
521533
properties;
522534
indexers;
523535
callProperties
@@ -644,7 +656,8 @@ end = struct
644656
let type_parameter_declaration =
645657
wrap (type_parameter_declaration ~allow_default:false)
646658
let type_parameter_instantiation = wrap type_parameter_instantiation
647-
let _object ?(allow_static=false) env = wrap (_object ~allow_static) env
659+
let _object ?(allow_static=false) env =
660+
wrap (_object ~allow_static ~allow_exact:false) env
648661
let function_param_list = wrap function_param_list
649662
let annotation = wrap annotation
650663
let annotation_opt = wrap annotation_opt

src/parser/spider_monkey_ast.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ and Type : sig
9090
}
9191
end
9292
type t = {
93+
exact: bool;
9394
properties: Property.t list;
9495
indexers: Indexer.t list;
9596
callProperties: CallProperty.t list;

src/parser/test/custom_ast_types.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,6 @@ def("DeclareFunction")
6666

6767
def("Function")
6868
.field("predicate", or(def("Predicate"), null), defaults["null"])
69+
70+
def("ObjectTypeAnnotation")
71+
.field("exact", Boolean)

src/parser/test/esprima_test_runner.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,9 @@ function handleSpecialObjectCompare(esprima, flow, env) {
217217
esprima.typeAnnotation = null;
218218
}
219219
break;
220+
case 'ObjectTypeAnnotation':
221+
esprima.exact = esprima.exact || false;
222+
break;
220223
case 'ObjectTypeProperty':
221224
case 'ObjectTypeIndexer':
222225
case 'ObjectTypeCallProperty':

src/parser/test/hardcoded_tests.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ module.exports = {
177177
'key.name': 'numVal',
178178
'value.type': 'NumberTypeAnnotation',
179179
}
180-
]
180+
],
181181
}
182182
}
183183
}
@@ -826,6 +826,20 @@ module.exports = {
826826
}
827827
}
828828
},
829+
'Exact object types': {
830+
'var obj: {| x: number, y: string |}': { // no trailing comma
831+
'body.0.declarations.0.id.typeAnnotation.typeAnnotation': {
832+
'type': 'ObjectTypeAnnotation',
833+
'exact': true,
834+
},
835+
},
836+
'var obj: {| x: number, y: string, |}': { // trailing comma
837+
'body.0.declarations.0.id.typeAnnotation.typeAnnotation': {
838+
'type': 'ObjectTypeAnnotation',
839+
'exact': true,
840+
},
841+
}
842+
},
829843
'Tuples': {
830844
'var a : [] = [];': {
831845
'body.0.declarations.0.id.typeAnnotation.typeAnnotation': {

src/typing/class_sig.ml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ let extract_mixins _cx =
559559
let mk_interface cx loc reason structural self = Ast.Statement.(
560560
fun { Interface.
561561
typeParameters;
562-
body = (_, { Ast.Type.Object.properties; indexers; callProperties });
562+
body = (_, { Ast.Type.Object.properties; indexers; callProperties; _ });
563563
extends;
564564
mixins;
565565
_;

src/typing/type_annotation.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -444,7 +444,7 @@ let rec convert cx tparams_map = Ast.Type.(function
444444
in
445445
if (tparams = []) then ft else PolyT(tparams, ft)
446446

447-
| loc, Object { Object.properties; indexers; callProperties; } ->
447+
| loc, Object { Object.exact; properties; indexers; callProperties; } ->
448448
let props_map = List.fold_left (
449449
fun props_map (loc, { Object.Property.key; value; optional; _ }) ->
450450
(match key with
@@ -513,7 +513,7 @@ let rec convert cx tparams_map = Ast.Type.(function
513513
let proto = MixedT (reason_of_string reason_desc, Mixed_everything) in
514514
let flags = {
515515
sealed = if sealed then Sealed else UnsealedInFile (Loc.source loc);
516-
exact = not sealed;
516+
exact = not sealed || exact;
517517
frozen = false;
518518
} in
519519
ObjT (mk_reason reason_desc loc,

0 commit comments

Comments
 (0)