diff --git a/src/typing/ty_normalizer.ml b/src/typing/ty_normalizer.ml index 3135ca6127e..2f2330198d8 100644 --- a/src/typing/ty_normalizer.ml +++ b/src/typing/ty_normalizer.ml @@ -550,6 +550,56 @@ end = struct end + + module Substitution = struct + module Env = struct + type t = Ty.t SMap.t + let descend _ e = e + end + module Visitor = Ty_visitor.MakeAny(Env) + + (* Replace a list of type parameters with a list of types in the given type. + * These lists might not match exactly in length. + *) + let run = + let open Ty in + let init_env tparams types = + let rec step acc = function + | [], _ | _, [] -> acc + | p::ps, t::ts -> step (SMap.add p.tp_name t acc) (ps, ts) + in + step SMap.empty (tparams, types) + in + let remove_params env = function + | None -> env + | Some ps -> List.fold_left (fun e p -> SMap.remove p.tp_name e) env ps + in + let open Visitor in + let visitor = object inherit c as super + method! type_ env t = + match t with + | TypeAlias { ta_tparams=ps; _ } + | Fun { fun_type_params=ps; _ } -> + let env = remove_params env ps in + super#type_ env t + | Bound (Symbol (_, name)) -> + begin match SMap.get name env with + | Some t' -> + (* Mark the substitution *) + begin tell true >>= fun _ -> + super#type_ env t' end + | _ -> super#type_ env t + end + | _ -> super#type_ env t + end + in + fun vs ts t_body -> + let env = init_env vs ts in + let t, changed = visitor#type_ env t_body in + if changed then simplify_unions_inters t else t + + end + (***********************) (* Construct built-ins *) (***********************) @@ -1076,9 +1126,17 @@ end = struct type__ ~env t >>= fun ty -> mapM (type__ ~env) targs >>= fun targs -> match ty with - | Ty.Class (name, _, _) - | Ty.TypeAlias { Ty.ta_name=name; _ } -> + | Ty.Class (name, _, _) -> return (Ty.generic_t name targs) + | Ty.TypeAlias { Ty.ta_name; ta_tparams; ta_type } -> + let t = if C.opt_expand_type_aliases then + match ta_tparams, ta_type with + | Some ps, Some t -> Substitution.run ps targs t + | None, Some t -> t + | _ -> Ty.generic_t ta_name targs + else + Ty.generic_t ta_name targs + in return t | Ty.Any -> return Ty.Any (* "Fix" type application on recursive types *) diff --git a/tests/type-at-pos/test.sh b/tests/type-at-pos/test.sh index 9cbaa13370c..9703b5a74c1 100644 --- a/tests/type-at-pos/test.sh +++ b/tests/type-at-pos/test.sh @@ -605,3 +605,14 @@ printf "type-alias.js:19:8 " assert_ok "$FLOW" type-at-pos type-alias.js 19 8 --strip-root --pretty printf "type-alias.js:20:8 " assert_ok "$FLOW" type-at-pos type-alias.js 20 8 --strip-root --pretty + +printf "type-alias.js:24:6 " +assert_ok "$FLOW" type-at-pos type-alias.js 24 6 --strip-root --pretty --expand-type-aliases +printf "type-alias.js:25:6 " +assert_ok "$FLOW" type-at-pos type-alias.js 25 6 --strip-root --pretty --expand-type-aliases +printf "type-alias.js:27:6 " +assert_ok "$FLOW" type-at-pos type-alias.js 27 6 --strip-root --pretty --expand-type-aliases +printf "type-alias.js:29:6 " +assert_ok "$FLOW" type-at-pos type-alias.js 29 6 --strip-root --pretty --expand-type-aliases +printf "type-alias.js:31:6 " +assert_ok "$FLOW" type-at-pos type-alias.js 31 6 --strip-root --pretty --expand-type-aliases diff --git a/tests/type-at-pos/type-alias.js b/tests/type-at-pos/type-alias.js index d8b56a23643..07383e70c34 100644 --- a/tests/type-at-pos/type-alias.js +++ b/tests/type-at-pos/type-alias.js @@ -19,3 +19,13 @@ function f() { type E = $Exact; type F = ?X; } + +type G = X | null; +type H = G; +type I = G | string> +type J = ((x: X) => void) | X | null; +type K = J; +type L = ((x: X, y: Y) => void) | X | Y | null; +type M = L; +type N = { x: N } | null; +type O = N diff --git a/tests/type-at-pos/type-at-pos.exp b/tests/type-at-pos/type-at-pos.exp index b9d86a26569..6ab9d0746fe 100644 --- a/tests/type-at-pos/type-at-pos.exp +++ b/tests/type-at-pos/type-at-pos.exp @@ -3573,3 +3573,78 @@ type-alias.js:20:8 { "start":8, "end":8 } +type-alias.js:24:6 { + "type":"type H = number | null", + "reasons":[], + "loc":{ + "source":"type-alias.js", + "type":"SourceFile", + "start":{"line":24,"column":6,"offset":364}, + "end":{"line":24,"column":6,"offset":365} + }, + "path":"type-alias.js", + "line":24, + "endline":24, + "start":6, + "end":6 +} +type-alias.js:25:6 { + "type":"type I = string | number | null", + "reasons":[], + "loc":{ + "source":"type-alias.js", + "type":"SourceFile", + "start":{"line":25,"column":6,"offset":384}, + "end":{"line":25,"column":6,"offset":385} + }, + "path":"type-alias.js", + "line":25, + "endline":25, + "start":6, + "end":6 +} +type-alias.js:27:6 { + "type":"type K = ((x: X) => void) | number | null", + "reasons":[], + "loc":{ + "source":"type-alias.js", + "type":"SourceFile", + "start":{"line":27,"column":6,"offset":459}, + "end":{"line":27,"column":6,"offset":460} + }, + "path":"type-alias.js", + "line":27, + "endline":27, + "start":6, + "end":6 +} +type-alias.js:29:6 { + "type":"type M = ((x: X, y: Z) => void) | number | Z | null", + "reasons":[], + "loc":{ + "source":"type-alias.js", + "type":"SourceFile", + "start":{"line":29,"column":6,"offset":536}, + "end":{"line":29,"column":6,"offset":537} + }, + "path":"type-alias.js", + "line":29, + "endline":29, + "start":6, + "end":6 +} +type-alias.js:31:6 { + "type":"type O = {x: ({x: N} | null)} | null", + "reasons":[], + "loc":{ + "source":"type-alias.js", + "type":"SourceFile", + "start":{"line":31,"column":6,"offset":594}, + "end":{"line":31,"column":6,"offset":595} + }, + "path":"type-alias.js", + "line":31, + "endline":31, + "start":6, + "end":6 +}