diff --git a/lib/core.js b/lib/core.js
index 255f4e89f8a..2715ed37776 100644
--- a/lib/core.js
+++ b/lib/core.js
@@ -608,3 +608,6 @@ declare var exports: any;
/* Commonly available, shared between node and dom */
declare var console: any;
+
+/* JSX Intrinsics */
+type $JSXIntrinsics = Object;
diff --git a/src/common/flowConfig.ml b/src/common/flowConfig.ml
index c856bb63463..8f242778c44 100644
--- a/src/common/flowConfig.ml
+++ b/src/common/flowConfig.ml
@@ -45,6 +45,7 @@ module Opts = struct
esproposal_class_instance_fields: esproposal_feature_mode;
esproposal_class_static_fields: esproposal_feature_mode;
esproposal_decorators: esproposal_feature_mode;
+ facebook_ignore_fbt: bool;
ignore_non_literal_requires: bool;
moduleSystem: moduleSystem;
module_name_mappers: (Str.regexp * string) list;
@@ -111,6 +112,7 @@ module Opts = struct
esproposal_class_instance_fields = ESPROPOSAL_WARN;
esproposal_class_static_fields = ESPROPOSAL_WARN;
esproposal_decorators = ESPROPOSAL_WARN;
+ facebook_ignore_fbt = false;
ignore_non_literal_requires = false;
moduleSystem = Node;
module_name_mappers = [];
@@ -490,54 +492,64 @@ let parse_excludes config lines =
let file_extension = Str.regexp "^\\(\\.[^ \t]+\\)+$"
-let parse_options config lines = Opts.(
- let options = Opts.parse config.options lines
- |> Opts.define_opt "esproposal.class_instance_fields" Opts.({
+let parse_options config lines =
+ let open Opts in
+ let options = parse config.options lines
+ |> define_opt "esproposal.class_instance_fields" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_esproposal_feature_flag ~allow_enable:true;
setter = (fun opts v -> {
opts with esproposal_class_instance_fields = v;
});
- })
+ }
- |> Opts.define_opt "esproposal.class_static_fields" Opts.({
+ |> define_opt "esproposal.class_static_fields" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_esproposal_feature_flag ~allow_enable:true;
setter = (fun opts v -> {
opts with esproposal_class_static_fields = v;
});
- })
+ }
- |> Opts.define_opt "esproposal.decorators" Opts.({
+ |> define_opt "esproposal.decorators" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_esproposal_feature_flag;
setter = (fun opts v -> {
opts with esproposal_decorators = v;
});
- })
+ }
- |> Opts.define_opt "log.file" Opts.({
+ |> define_opt "facebook.ignore_fbt" {
+ _initializer = USE_DEFAULT;
+ flags = [];
+ optparser = optparse_boolean;
+ setter = (fun opts v -> {
+ opts with facebook_ignore_fbt = v;
+ });
+ }
+
+ |> define_opt "log.file" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_filepath;
setter = (fun opts v -> {
opts with log_file = Some v;
});
- })
+ }
- |> Opts.define_opt "module.ignore_non_literal_requires" Opts.({
+ |> define_opt "module.ignore_non_literal_requires" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_boolean;
setter = (fun opts v ->
{opts with ignore_non_literal_requires = v;}
);
- })
+ }
- |> Opts.define_opt "module.file_ext" Opts.({
+ |> define_opt "module.file_ext" {
_initializer = INIT_FN (fun opts -> {
opts with module_file_exts = SSet.empty;
});
@@ -555,9 +567,9 @@ let parse_options config lines = Opts.(
let module_file_exts = SSet.add v opts.module_file_exts in
{opts with module_file_exts;}
);
- })
+ }
- |> Opts.define_opt "module.name_mapper" Opts.({
+ |> define_opt "module.name_mapper" {
_initializer = USE_DEFAULT;
flags = [ALLOW_DUPLICATE];
optparser = (fun str ->
@@ -580,9 +592,9 @@ let parse_options config lines = Opts.(
let module_name_mappers = v :: opts.module_name_mappers in
{opts with module_name_mappers;}
);
- })
+ }
- |> Opts.define_opt "module.system" Opts.({
+ |> define_opt "module.system" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_enum [
@@ -592,9 +604,9 @@ let parse_options config lines = Opts.(
setter = (fun opts v -> {
opts with moduleSystem = v;
});
- })
+ }
- |> Opts.define_opt "module.system.node.resolve_dirname" Opts.({
+ |> define_opt "module.system.node.resolve_dirname" {
_initializer = INIT_FN (fun opts -> {
opts with node_resolver_dirnames = [];
});
@@ -604,84 +616,83 @@ let parse_options config lines = Opts.(
let node_resolver_dirnames = v :: opts.node_resolver_dirnames in
{opts with node_resolver_dirnames;}
);
- })
+ }
- |> Opts.define_opt "munge_underscores" Opts.({
+ |> define_opt "munge_underscores" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_boolean;
setter = (fun opts v ->
{opts with munge_underscores = v;}
);
- })
+ }
- |> Opts.define_opt "server.max_workers" Opts.({
+ |> define_opt "server.max_workers" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_uint;
setter = (fun opts v ->
{opts with max_workers = v;}
);
- })
+ }
- |> Opts.define_opt "strip_root" Opts.({
+ |> define_opt "strip_root" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_boolean;
setter = (fun opts v ->
{opts with strip_root = v;}
);
- })
+ }
- |> Opts.define_opt "suppress_comment" Opts.({
+ |> define_opt "suppress_comment" {
_initializer = USE_DEFAULT;
flags = [ALLOW_DUPLICATE];
optparser = optparse_regexp;
setter = (fun opts v -> {
opts with suppress_comments = v::(opts.suppress_comments);
});
- })
+ }
- |> Opts.define_opt "suppress_type" Opts.({
+ |> define_opt "suppress_type" {
_initializer = USE_DEFAULT;
flags = [ALLOW_DUPLICATE];
optparser = optparse_string;
setter = (fun opts v -> {
opts with suppress_types = SSet.add v opts.suppress_types;
});
- })
+ }
- |> Opts.define_opt "temp_dir" Opts.({
+ |> define_opt "temp_dir" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_filepath;
setter = (fun opts v -> {
opts with temp_dir = v;
});
- })
+ }
- |> Opts.define_opt "traces" Opts.({
+ |> define_opt "traces" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_uint;
setter = (fun opts v ->
{opts with traces = v;}
);
- })
+ }
- |> Opts.define_opt "unsafe.enable_getters_and_setters" Opts.({
+ |> define_opt "unsafe.enable_getters_and_setters" {
_initializer = USE_DEFAULT;
flags = [];
optparser = optparse_boolean;
setter = (fun opts v ->
{opts with enable_unsafe_getters_and_setters = v;}
);
- })
+ }
|> get_defined_opts
in
{config with options}
-)
let assert_version (ln, line) =
try
diff --git a/src/common/flowConfig.mli b/src/common/flowConfig.mli
index 3130a33ce0b..f021ff6ec83 100644
--- a/src/common/flowConfig.mli
+++ b/src/common/flowConfig.mli
@@ -19,6 +19,7 @@ module Opts : sig
esproposal_class_instance_fields: esproposal_feature_mode;
esproposal_class_static_fields: esproposal_feature_mode;
esproposal_decorators: esproposal_feature_mode;
+ facebook_ignore_fbt: bool;
ignore_non_literal_requires: bool;
moduleSystem: moduleSystem;
module_name_mappers: (Str.regexp * string) list;
diff --git a/src/typing/debug_js.ml b/src/typing/debug_js.ml
index 39b9333c13f..8089ab401ed 100644
--- a/src/typing/debug_js.ml
+++ b/src/typing/debug_js.ml
@@ -340,6 +340,10 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
"type", _json_of_t json_cx t
]
+ | ReifyTypeT (_, t_out) -> [
+ "t_out", _json_of_t json_cx t_out
+ ]
+
| SpecializeT (_, cache, targs, tvar) -> [
"cache", JSON_Bool cache;
"types", JSON_Array (List.map (_json_of_t json_cx) targs);
@@ -454,6 +458,7 @@ and _json_of_use_t_impl json_cx t = Hh_json.(
"target_module_t", _json_of_t json_cx target_module_t;
"t_out", _json_of_t json_cx t_out;
]
+ | DebugPrintT _reason -> []
)
)
@@ -743,9 +748,16 @@ let json_of_t ?(depth=1000) cx t =
let json_cx = { cx; depth; stack = ISet.empty; } in
_json_of_t json_cx t
+let json_of_use_t ?(depth=1000) cx use_t =
+ let json_cx = { cx; depth; stack = ISet.empty; } in
+ _json_of_use_t json_cx use_t
+
let jstr_of_t ?(depth=1000) cx t =
Hh_json.json_to_multiline (json_of_t ~depth cx t)
+let jstr_of_use_t ?(depth=1000) cx use_t =
+ Hh_json.json_to_multiline (json_of_use_t ~depth cx use_t)
+
let json_of_graph ?(depth=1000) cx = Hh_json.(
let entries = IMap.fold (fun id _ entries ->
let json_cx = { cx; depth; stack = ISet.empty; } in
diff --git a/src/typing/debug_js.mli b/src/typing/debug_js.mli
index c12a8f1876f..e74189b7f2f 100644
--- a/src/typing/debug_js.mli
+++ b/src/typing/debug_js.mli
@@ -10,6 +10,8 @@
val json_of_t: ?depth:int -> Context.t -> Type.t -> Hh_json.json
val jstr_of_t: ?depth:int -> Context.t -> Type.t -> string
+val json_of_use_t: ?depth:int -> Context.t -> Type.use_t -> Hh_json.json
+val jstr_of_use_t: ?depth:int -> Context.t -> Type.use_t -> string
val json_of_graph: ?depth:int -> Context.t -> Hh_json.json
val jstr_of_graph: ?depth:int -> Context.t -> string
val json_of_scope: ?depth:int -> Context.t -> Scope.t -> Hh_json.json
diff --git a/src/typing/flow_js.ml b/src/typing/flow_js.ml
index 9e2804e7c3f..0d0becd6c06 100644
--- a/src/typing/flow_js.ml
+++ b/src/typing/flow_js.ml
@@ -1114,6 +1114,14 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
| l, UseT DestructuringT (reason, t, s) ->
rec_flow_t cx trace (l, eval_selector cx reason t s)
+ (*************)
+ (* Debugging *)
+ (*************)
+
+ | (_, DebugPrintT (reason)) ->
+ let msg = (spf "!!! DebugPrintT: %s" (Debug_js.jstr_of_t cx l)) in
+ add_error cx [reason, msg];
+
(************)
(* tainting *)
(************)
@@ -2393,6 +2401,20 @@ let rec __flow cx ((l: Type.t), (u: Type.use_t)) trace =
| (TypeT(_,l), UseT TypeT(_,u)) ->
rec_unify cx trace l u
+ | (TypeT(type_reason, l), GetPropT(get_reason, (prop_reason, name), t_out)) ->
+ (**
+ * Reify the type, extract the prop from that, then wrap that result in a
+ * TypeT again.
+ *)
+ let prop_t = mk_tvar_where cx get_reason (fun t ->
+ rec_flow cx trace (l, GetPropT(get_reason, (prop_reason, name), t))
+ ) in
+ rec_flow cx trace (TypeT (type_reason, mk_typeof_annotation cx ~trace prop_t), T t_out)
+
+ | (TypeT(_, l), ReifyTypeT(_, t_out)) ->
+ (* Extract the type denoted by the type expression *)
+ rec_flow cx trace (l, T t_out)
+
(* non-class/function values used in annotations are errors *)
| _, UseT TypeT _ ->
prerr_flow cx trace
@@ -3563,6 +3585,7 @@ and err_operation = function
| HasOwnPropT _ -> "Property not found in"
| HasPropT _ -> "Property not found in"
| UnaryMinusT _ -> "Expected number instead of"
+ | ReifyTypeT _ -> "Internal Error: Invalid type applied to ReifyTypeT!"
(* unreachable or unclassified use-types. until we have a mechanical way
to verify that all legit use types are listed above, we can't afford
to throw on a use type, so mark the error instead *)
diff --git a/src/typing/gc_js.ml b/src/typing/gc_js.ml
index 4781bd931c0..b3a8c5f9e8b 100644
--- a/src/typing/gc_js.ml
+++ b/src/typing/gc_js.ml
@@ -287,6 +287,9 @@ and gc_use cx state = function
| NotT (_, t) ->
gc cx state t
+ | ReifyTypeT (_, t_out) ->
+ gc cx state t_out
+
| SpecializeT (_, _, ts, t) ->
ts |> List.iter (gc cx state);
gc cx state t
@@ -373,6 +376,8 @@ and gc_use cx state = function
gc cx state target_module;
gc cx state t_out;
+ | DebugPrintT _ -> ()
+
and gc_id cx state id =
let root_id, constraints = Flow_js.find_constraints cx id in (
if state#mark id then (
diff --git a/src/typing/type.ml b/src/typing/type.ml
index 4c87b6327dc..709e65104c3 100644
--- a/src/typing/type.ml
+++ b/src/typing/type.ml
@@ -269,6 +269,16 @@ and use_t =
| OrT of reason * t * t
| NotT of reason * t
+ (**
+ * A special (internal-only) type used to "reify" a TypeT back down to it's
+ * value-space type. One way to think of this is as the inverse of `typeof`.
+ *
+ * Note that there is no syntax for specifying this type. To surface such a
+ * type would result in code that cannot be compiled without extensive type
+ * info (or possibly at all!).
+ *)
+ | ReifyTypeT of reason * t_out
+
(* operation on polymorphic types *)
(** SpecializeT(_, cache, targs, tresult) instantiates a polymorphic type with
type arguments targs, and flows the result into tresult. If cache is set,
@@ -337,6 +347,8 @@ and use_t =
| SetNamedExportsT of reason * t SMap.t * t_out
| SetStarExportsT of reason * t * t_out
+ | DebugPrintT of reason
+
(** Here's to the crazy ones. The misfits. The rebels. The troublemakers. The
round pegs in the square holes. **)
@@ -718,6 +730,7 @@ and reason_of_use_t = function
| AndT (reason, _, _)
| OrT (reason, _, _)
| NotT (reason, _)
+ | ReifyTypeT (reason, _)
| BecomeT (reason, _)
| ReposLowerT (reason, _)
-> reason
@@ -773,6 +786,7 @@ and reason_of_use_t = function
| CJSExtractNamedExportsT (reason, _, _) -> reason
| SetNamedExportsT (reason, _, _) -> reason
| SetStarExportsT (reason, _, _) -> reason
+ | DebugPrintT reason -> reason
(* helper: we want the tvar id as well *)
(* NOTE: uncalled for now, because ids are nondetermistic
@@ -928,6 +942,8 @@ and mod_reason_of_use_t f = function
| OrT (reason, t1, t2) -> OrT (f reason, t1, t2)
| NotT (reason, t) -> NotT (f reason, t)
+ | ReifyTypeT (reason, t_out) -> ReifyTypeT (f reason, t_out)
+
| SpecializeT(reason, cache, ts, t) -> SpecializeT (f reason, cache, ts, t)
| ThisSpecializeT(reason, this, t) -> ThisSpecializeT (f reason, this, t)
@@ -968,6 +984,7 @@ and mod_reason_of_use_t f = function
| CJSExtractNamedExportsT (reason, t1, t2) -> CJSExtractNamedExportsT (f reason, t1, t2)
| SetNamedExportsT (reason, tmap, t_out) -> SetNamedExportsT(f reason, tmap, t_out)
| SetStarExportsT (reason, target_module_t, t_out) -> SetStarExportsT(f reason, target_module_t, t_out)
+ | DebugPrintT reason -> DebugPrintT (f reason)
(* type comparison mod reason *)
@@ -1073,6 +1090,7 @@ let string_of_use_ctor = function
| AndT _ -> "AndT"
| OrT _ -> "OrT"
| NotT _ -> "NotT"
+ | ReifyTypeT _ -> "ReifyTypeT"
| SpecializeT _ -> "SpecializeT"
| ThisSpecializeT _ -> "ThisSpecializeT"
| VarianceCheckT _ -> "VarianceCheckT"
@@ -1100,6 +1118,7 @@ let string_of_use_ctor = function
| CJSExtractNamedExportsT _ -> "CJSExtractNamedExportsT"
| SetNamedExportsT _ -> "SetNamedExportsT"
| SetStarExportsT _ -> "SetStarExportsT"
+ | DebugPrintT _ -> "DebugPrintT"
let string_of_binary_test = function
| Instanceof -> "instanceof"
diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml
index 9866e85e3a2..c842f51d3a6 100644
--- a/src/typing/type_inference_js.ml
+++ b/src/typing/type_inference_js.ml
@@ -4307,7 +4307,13 @@ and jsx cx type_params_map = Ast.JSX.(function { openingElement; closingElement;
and jsx_title cx type_params_map openingElement children = Ast.JSX.(
let eloc, { Opening.name; attributes; _ } = openingElement in
+ let facebook_ignore_fbt =
+ FlowConfig.((get_unsafe ()).options.Opts.facebook_ignore_fbt)
+ in
match name with
+ | Identifier (_, { Identifier.name })
+ when name = "fbt" && facebook_ignore_fbt ->
+ AnyT.why (mk_reason "" eloc)
| Identifier (_, { Identifier.name }) when name = String.capitalize name ->
let reason = mk_reason (spf "React element `%s`" name) eloc in
@@ -4366,12 +4372,60 @@ and jsx_title cx type_params_map openingElement children = Ast.JSX.(
)
| Identifier (_, { Identifier.name }) ->
+ (**
+ * For JSX intrinsics, we assume a built-in global
+ * object type: $JSXIntrinsics. The keys of this object type correspond to
+ * each JSX intrinsic name, and the type of the value for that key is the
+ * type signature of the intrinsic ReactComponent.
+ *
+ * We use a single object type for this (rather than several individual
+ * globals) to allow for a default `type $JSXIntrinsics = Object;` that
+ * ships with lib/core.js. This allows JSX to work out of the box where
+ * all intrinsics are typed as `any`. Users can then refine the set of
+ * intrinsics their application uses with a more specific libdef.
+ *)
+ let jsx_intrinsics =
+ Env_js.get_var
+ ~lookup_mode:Env_js.LookupMode.ForType
+ cx
+ "$JSXIntrinsics"
+ (mk_reason "JSX Intrinsics lookup" eloc)
+ in
+
+ (**
+ * Because $JSXIntrinsics is a type alias, extracting a property off of it
+ * will result in a TypeT as well. This presents a problem because we need
+ * a value type that can be passed in to React.creatElement; So we first
+ * reify the TypeT into it's value, then pass this along.
+ *
+ * This is a bit strange but it's fallout from the decision to model
+ * $JSXIntrinsics using a type alias rather than a "value". Modeling with
+ * a value would be disingenous because no such value really exists (JSX
+ * intrinsics are just React components that are implicitly defined
+ * dynamically in library code such as `React.createElement`)
+ *)
+ let component_t_reason =
+ mk_reason (spf "JSX Intrinsic: `%s`" name) eloc
+ in
+ let component_t = Flow_js.mk_tvar_where cx component_t_reason (fun t ->
+ let prop_t = get_prop
+ ~is_cond:false
+ cx
+ component_t_reason
+ jsx_intrinsics
+ (component_t_reason, name)
+ in
+ Flow_js.flow cx (prop_t, ReifyTypeT(component_t_reason, t))
+ ) in
+
+ let attr_map = ref SMap.empty in
+ let spread = ref None in
attributes |> List.iter (function
| Opening.Attribute (aloc, { Attribute.
- name = Attribute.Identifier (_, { Identifier.name = aname });
- value
- }) ->
- ignore (match value with
+ name = Attribute.Identifier (_, { Identifier.name = aname });
+ value
+ }) ->
+ let attr_type = (match value with
| Some (Attribute.Literal (loc, lit)) ->
literal cx loc lit
| Some (Attribute.ExpressionContainer (_, {
@@ -4381,16 +4435,40 @@ and jsx_title cx type_params_map openingElement children = Ast.JSX.(
| _ ->
(* empty or nonexistent attribute values *)
EmptyT.at aloc
- )
+ ) in
+
+ if not (react_ignore_attribute aname)
+ then attr_map := !attr_map |> SMap.add aname attr_type
| Opening.Attribute _ ->
() (* TODO: attributes with namespaced names *)
| Opening.SpreadAttribute (aloc, { SpreadAttribute.argument }) ->
- () (* TODO: spread attributes *)
+ let spread_t = expression cx type_params_map argument in
+ spread := Some spread_t
);
- AnyT.t
+ let reason_props = prefix_reason "props of " component_t_reason in
+ let o = Flow_js.mk_object_with_map_proto cx reason_props
+ !attr_map (MixedT reason_props)
+ in
+ let o = match !spread with
+ | None -> o
+ | Some ex_t ->
+ let reason_prop = prefix_reason "spread of " (reason_of_t ex_t) in
+ clone_object_with_excludes cx reason_prop o ex_t react_ignored_attributes
+ in
+ (* TODO: children *)
+ let react = require cx "react" eloc in
+ let reason = mk_reason (spf "React element: `%s`" name) eloc in
+ Flow_js.mk_tvar_where cx reason (fun tvar ->
+ let reason_createElement = mk_reason "property `createElement`" eloc in
+ Flow_js.flow cx (react, MethodT(
+ reason,
+ (reason_createElement, "createElement"),
+ Flow_js.mk_methodtype react [component_t;o] tvar
+ ))
+ )
| _ ->
(* TODO? covers namespaced names, member expressions as element names *)
diff --git a/src/typing/type_visitor.ml b/src/typing/type_visitor.ml
index 67f3983767e..c9e7678f1b4 100644
--- a/src/typing/type_visitor.ml
+++ b/src/typing/type_visitor.ml
@@ -175,6 +175,7 @@ class ['a] t = object(self)
| AndT (_, _, _)
| OrT (_, _, _)
| NotT (_, _)
+ | ReifyTypeT (_, _)
| SpecializeT (_, _, _, _)
| ThisSpecializeT (_, _, _)
| VarianceCheckT (_, _, _)
@@ -202,6 +203,7 @@ class ['a] t = object(self)
| CJSExtractNamedExportsT (_, _, _)
| SetNamedExportsT (_, _, _)
| SetStarExportsT (_, _, _)
+ | DebugPrintT (_)
-> self#__TODO__ cx acc
(* The default behavior here could be fleshed out a bit, to look up the graph,
diff --git a/tests/facebook_ignore_fbt_false/.flowconfig b/tests/facebook_ignore_fbt_false/.flowconfig
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/facebook_ignore_fbt_false/facebook_ignore_fbt_false.exp b/tests/facebook_ignore_fbt_false/facebook_ignore_fbt_false.exp
new file mode 100644
index 00000000000..94a134f886e
--- /dev/null
+++ b/tests/facebook_ignore_fbt_false/facebook_ignore_fbt_false.exp
@@ -0,0 +1,8 @@
+
+main.js:4:2,8: React element: `fbt`
+Error:
+main.js:4:2,8: ReactElement
+This type is incompatible with
+main.js:4:11,16: number
+
+Found 1 error
diff --git a/tests/facebook_ignore_fbt_false/main.js b/tests/facebook_ignore_fbt_false/main.js
new file mode 100644
index 00000000000..e180aba3d63
--- /dev/null
+++ b/tests/facebook_ignore_fbt_false/main.js
@@ -0,0 +1,4 @@
+// @flow
+
+(: ReactElement);
+(: number); // Error: ReactElement ~> number
diff --git a/tests/facebook_ignore_fbt_true/.flowconfig b/tests/facebook_ignore_fbt_true/.flowconfig
new file mode 100644
index 00000000000..d8a3e5e93c3
--- /dev/null
+++ b/tests/facebook_ignore_fbt_true/.flowconfig
@@ -0,0 +1,2 @@
+[options]
+facebook.ignore_fbt=true
diff --git a/tests/facebook_ignore_fbt_true/facebook_ignore_fbt_true.exp b/tests/facebook_ignore_fbt_true/facebook_ignore_fbt_true.exp
new file mode 100644
index 00000000000..c82edb53368
--- /dev/null
+++ b/tests/facebook_ignore_fbt_true/facebook_ignore_fbt_true.exp
@@ -0,0 +1,2 @@
+
+Found 0 errors
diff --git a/tests/facebook_ignore_fbt_true/main.js b/tests/facebook_ignore_fbt_true/main.js
new file mode 100644
index 00000000000..f3a1ede29c4
--- /dev/null
+++ b/tests/facebook_ignore_fbt_true/main.js
@@ -0,0 +1,4 @@
+// @flow
+
+(: number);
+(: string);
diff --git a/tests/jsx_intrinsics.builtin/.flowconfig b/tests/jsx_intrinsics.builtin/.flowconfig
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/tests/jsx_intrinsics.builtin/jsx_intrinsics.builtin.exp b/tests/jsx_intrinsics.builtin/jsx_intrinsics.builtin.exp
new file mode 100644
index 00000000000..5a2af59e46b
--- /dev/null
+++ b/tests/jsx_intrinsics.builtin/jsx_intrinsics.builtin.exp
@@ -0,0 +1,10 @@
+
+main.js:6:10,8:3: property `prop`
+Property not found in
+main.js:12:26,40: object type
+
+main.js:12:26,40: property `prop1`
+Property not found in
+main.js:6:10,8:3: object type
+
+Found 2 errors
diff --git a/tests/jsx_intrinsics.builtin/main.js b/tests/jsx_intrinsics.builtin/main.js
new file mode 100644
index 00000000000..a486f17d9ae
--- /dev/null
+++ b/tests/jsx_intrinsics.builtin/main.js
@@ -0,0 +1,15 @@
+// @flow
+
+var React = require('react');
+
+class CustomComponent extends React.Component {
+ props: {
+ prop: string
+ };
+}
+
+var a: ReactElement = ;
+var b: ReactElement = ; // Error: Props<{prop}> ~> Props<{prop1}>
+
+// Not an error because intrinsics are typed as `any` out of the box!
+var c: ReactElement = ;
diff --git a/tests/jsx_intrinsics.custom/.flowconfig b/tests/jsx_intrinsics.custom/.flowconfig
new file mode 100644
index 00000000000..56ce8c40d88
--- /dev/null
+++ b/tests/jsx_intrinsics.custom/.flowconfig
@@ -0,0 +1,2 @@
+[libs]
+lib
diff --git a/tests/jsx_intrinsics.custom/jsx_intrinsics.custom.exp b/tests/jsx_intrinsics.custom/jsx_intrinsics.custom.exp
new file mode 100644
index 00000000000..8d29866f3c8
--- /dev/null
+++ b/tests/jsx_intrinsics.custom/jsx_intrinsics.custom.exp
@@ -0,0 +1,22 @@
+
+main.js:6:10,8:3: property `prop`
+Property not found in
+main.js:12:26,40: object type
+
+main.js:12:26,40: property `prop1`
+Property not found in
+main.js:6:10,8:3: object type
+
+main.js:15:1,15: type parameter `Props` of React element: `div`
+Error:
+main.js:15:10,11: number
+This type is incompatible with
+[LIB] lib/jsx.js:4:27,32: string
+
+main.js:17:47,63: React element: `div`
+Error:
+main.js:17:31,36: number
+This type is incompatible with
+[LIB] lib/jsx.js:4:27,32: string
+
+Found 4 errors
diff --git a/tests/jsx_intrinsics.custom/lib/jsx.js b/tests/jsx_intrinsics.custom/lib/jsx.js
new file mode 100644
index 00000000000..e01bab582b0
--- /dev/null
+++ b/tests/jsx_intrinsics.custom/lib/jsx.js
@@ -0,0 +1,6 @@
+type $JSXIntrinsic = Class>;
+
+type $JSXIntrinsics = {
+ div: $JSXIntrinsic<{id: string}>,
+ span: $JSXIntrinsic<{id: string, class: string}>,
+};
diff --git a/tests/jsx_intrinsics.custom/main.js b/tests/jsx_intrinsics.custom/main.js
new file mode 100644
index 00000000000..556397aabf4
--- /dev/null
+++ b/tests/jsx_intrinsics.custom/main.js
@@ -0,0 +1,17 @@
+// @flow
+
+var React = require('react');
+
+class CustomComponent extends React.Component {
+ props: {
+ prop: string
+ };
+}
+
+var a: ReactElement = ;
+var b: ReactElement = ; // Error: Props<{prop}> ~> Props<{prop1}>
+
+;
+; // Error: (`id` prop) number ~> string
+var c: ReactElement = ;
+var d: ReactElement = ; // Error: Props<{id:string}> ~> Props<{id:number}>
diff --git a/tests/more_react/API.react.js b/tests/more_react/API.react.js
index 71e7692f6c5..9cb696f75b9 100644
--- a/tests/more_react/API.react.js
+++ b/tests/more_react/API.react.js
@@ -5,4 +5,4 @@ app.setProps({y:42}); // error, y:number but foo expects string in App.react
app.setState({z:42}); // error, z:number but foo expects string in App.react
function bar(x:number) { }
-bar(app.props.children); // error, app.props.children: string
+bar(app.props.children); // error, app.props.children: mixed ~> number
diff --git a/tests/more_react/more_react.exp b/tests/more_react/more_react.exp
index a54d69a053e..191114df056 100644
--- a/tests/more_react/more_react.exp
+++ b/tests/more_react/more_react.exp
@@ -7,17 +7,24 @@ API.react.js:5:5,12: property `setState`
Property not found in
API.react.js:5:1,3: ReactElement
-App.react.js:26:21,21: return
-Missing annotation
+API.react.js:8:15,22: property `children`
+Property not found in
+API.react.js:8:5,13: props of React component
App.react.js:36:14,14: number
This type is incompatible with
App.react.js:10:16,21: string
+JSX.js:8:3,14: type parameter `Props` of React element `App`
+Error:
+JSX.js:8:11,12: number
+This type is incompatible with
+App.react.js:14:37,42: string
+
propTypes.js:15:1,18:2: React element `D`
Error:
propTypes.js:9:3,11: property `name`
Property not found in
propTypes.js:15:1,18:2: type parameter `Props` of React element `D`
-Found 5 errors
+Found 6 errors
diff --git a/tests/qualified/qualified.exp b/tests/qualified/qualified.exp
index 610eb95dea1..9e66553299a 100644
--- a/tests/qualified/qualified.exp
+++ b/tests/qualified/qualified.exp
@@ -3,4 +3,8 @@ qualified.js:4:13,13: number
This type is incompatible with
qualified.js:4:7,9: C
-Found 1 error
+qualified.js:10:14,19: string
+This type is incompatible with
+qualified.js:10:8,10: number
+
+Found 2 errors
diff --git a/tests/qualified/qualified.js b/tests/qualified/qualified.js
index b64bf03032a..34233171584 100644
--- a/tests/qualified/qualified.js
+++ b/tests/qualified/qualified.js
@@ -2,3 +2,9 @@ class C { }
var M = { C: C };
var x:M.C = 0;
+
+type foo = {bar: number};
+type bar = foo.bar;
+
+var a: bar = 42;
+var b: bar = 'asdf'; // Error: string ~> number