From c9ddfd6b04713cd210fc764a0b364df5018af48e Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Thu, 6 Aug 2015 08:11:04 -1000 Subject: [PATCH 1/7] generators Behavior: Generators are iterable. Each value yielded from the generator function is iterated. Generators are iterators. Values can be passed in through the `next` method. The iterator result values will be either a) the yielded values, b) the value returned from the generator (the completion value) or c) `undefined`, if `next` is called after completion. Generators can delegate to other iterables, including other generators. Values passed to `next` on the delegator generator will be passed through to the delegatee. Similarly, values yielded by the delegatee will be returned along the iterable/iterator interfaces. The "delegate yield" expression, `yield *`, returns the completion value of the delegatee. Implementation: While normal functions' return type is the type of the `return` statement, generators are different. The return type is always a Generator, where the second type argument is the type of the `return` statement. We use the same process to infer this type--an internal tvar named "return"--but then override the return type when creating the function type, if the function is a generator. In order to track the `next` and `yield` types, we introduce new internal tvars, which accumulate lower bounds according to their use, and upper bounds from explicit annotation of the Generator type. Caveats: The generator interface is highly dynamic, and not particularly amenable to static typing. The type of Generator enforces that all next values and all yielded values be described by a single type, but it's quite normal to use values of different types. Consider the following example: ```javascript function *userGen() { var name = yield("What is your name?"); var age = yield("How old are you?"); return { name, age } } var gen = userGen(); var name, age, user; if (name = prompt(gen.next().value)) { if (age = prompt(gen.next(name).value)) { user = gen.next(parseInt(age, 10)).value; // user is { name: string|number, age: string|number } // but we "want" { name: string, age: number } } } ``` To escape this pitfall, you can use `Generator` or write the necessary dynamic type tests. Ideally, Flow would enforce that you first pass a `string` to `next`, then a `number`, but that isn't possible in general, and in specific instances is still very hard, so I'm leaving it for future work (if ever). Another difficulty is the fact that the argument to `next` is optional. This is because, in order to "start" the generator, you need to call `next` once. Put differently, the first argument to `next` is not returned by the first `yield` expression; the second argument to `next` is. Consider the following example: ```javascript function *bmiCalc() { var height: number = yield("What is your height, in meters?"); var weight: number = yield("What is your weight, in kilograms?"); return weight / (height * height); } // The first argument to next is always `void`. The value of this // expression is the string "What is your height..." bmiCalc.next(); // This call to `next` expects the value for `height`, above, but // because the type of `next` marks its argument optional, we allow // this. :( bmiCalc.next(); ``` In this implementation, I wanted to make things strict, so the return type of a yield expression is always `Y|void`, and the generator needs to do a dynamic type test in order to get at the expected `Y`. The above caveats, and the strict interpretation of this implementation, has the consequence that most, if not all, generator code "in the wild" will result in type errors from Flow. We might consider sacrificing correctness here for practical purposes, but I think it will be easier to go from strict to less strict than the other way around. --- lib/core.js | 22 ++++-- src/typing/type_inference_js.ml | 123 ++++++++++++++++++++++++-------- tests/async/async.exp | 14 ++-- tests/forof/forof.exp | 4 +- tests/generators/.flowconfig | 7 ++ tests/generators/generators.exp | 50 +++++++++++++ tests/generators/generators.js | 106 +++++++++++++++++++++++++++ tests/iterable/iterable.exp | 2 +- tests/promises/promises.exp | 26 +++---- tests/union/union.exp | 2 +- 10 files changed, 297 insertions(+), 59 deletions(-) create mode 100644 tests/generators/.flowconfig create mode 100644 tests/generators/generators.exp create mode 100644 tests/generators/generators.js diff --git a/lib/core.js b/lib/core.js index 8c748b193b4..d46b10b3c52 100644 --- a/lib/core.js +++ b/lib/core.js @@ -295,18 +295,26 @@ declare class JSON { } /* Iterators */ -interface IteratorResult { +interface IteratorResult { done: boolean; - value?: T; + value?: Y|R; } -interface Iterator { - next(): IteratorResult; - @@iterator(): Iterator; +interface $Iterator { + @@iterator(): $Iterator; + next(value?: N): IteratorResult; } +type Iterator = $Iterator; -interface Iterable { - @@iterator(): Iterator; +interface $Iterable { + @@iterator(): $Iterator; +} +type Iterable = $Iterable; + +/* Generators */ +interface Generator { + @@iterator(): $Iterator; + next(value?: N): IteratorResult; } /* Maps and Sets */ diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 845c6d3ab46..22f2cdbc92e 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -801,7 +801,6 @@ let rec convert cx map = Ast.Type.(function mk_nominal_type cx reason map (c, params) ) - (* TODO: unsupported generators *) | loc, Function { Function.params; returnType; rest; typeParameters } -> let typeparams, map_ = mk_type_param_declarations cx ~map typeParameters in let map = SMap.fold SMap.add map_ map in @@ -1976,8 +1975,8 @@ and statement cx = Ast.Statement.( let o = Flow_js.get_builtin_typeapp cx (mk_reason "iteration expected on Iterable" loc) - "Iterable" - [element_tvar] in + "$Iterable" + [element_tvar; AnyT.at loc; AnyT.at loc] in Flow_js.flow cx (t, o); (* null/undefined are NOT allowed *) @@ -2031,11 +2030,11 @@ and statement cx = Ast.Statement.( | (loc, Debugger) -> () - (* TODO: unsupported generators *) | (loc, FunctionDeclaration { FunctionDeclaration.id; params; defaults; rest; body; + generator; returnType; typeParameters; async; @@ -2043,7 +2042,7 @@ and statement cx = Ast.Statement.( }) -> let reason = mk_reason "function" loc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let fn_type = mk_function None cx reason ~async + let fn_type = mk_function None cx reason ~async ~generator typeParameters (params, defaults, rest) returnType body this in Hashtbl.replace cx.type_table loc fn_type; @@ -2609,11 +2608,11 @@ and object_prop cx map = Ast.Expression.Object.(function _ }) -> Ast.Expression.Function.( let { params; defaults; rest; body; - returnType; typeParameters; id; async; _ } = func + returnType; typeParameters; id; async; generator; _ } = func in let reason = mk_reason "function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let ft = mk_function id cx ~async reason typeParameters + let ft = mk_function id cx ~async ~generator reason typeParameters (params, defaults, rest) returnType body this in Hashtbl.replace cx.type_table vloc ft; @@ -2663,7 +2662,7 @@ and object_prop cx map = Ast.Expression.Object.(function let { body; returnType; _ } = func in let reason = mk_reason "getter function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let function_type = mk_function None cx ~async:false reason None + let function_type = mk_function None cx ~async:false ~generator:false reason None ([], [], None) returnType body this in let return_t = extract_getter_type function_type in @@ -2688,7 +2687,7 @@ and object_prop cx map = Ast.Expression.Object.(function let { params; defaults; body; returnType; _ } = func in let reason = mk_reason "setter function" vloc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - let function_type = mk_function None cx ~async:false reason None + let function_type = mk_function None cx ~async:false ~generator:false reason None (params, defaults, None) returnType body this in let param_t = extract_setter_type function_type in @@ -3360,6 +3359,7 @@ and expression_ ~is_cond cx loc e = Ast.Expression.(match e with params; defaults; rest; body; async; + generator; returnType; typeParameters; _ @@ -3367,15 +3367,15 @@ and expression_ ~is_cond cx loc e = Ast.Expression.(match e with let desc = (if async then "async " else "") ^ "function" in let reason = mk_reason desc loc in let this = Flow_js.mk_tvar cx (replace_reason "this" reason) in - mk_function id cx reason ~async + mk_function id cx reason ~async ~generator typeParameters (params, defaults, rest) returnType body this - (* TODO: unsupported generators *) | ArrowFunction { ArrowFunction.id; params; defaults; rest; body; async; + generator; returnType; typeParameters; _ @@ -3384,8 +3384,8 @@ and expression_ ~is_cond cx loc e = Ast.Expression.(match e with let reason = mk_reason desc loc in let this = this_ cx reason in let super = super_ cx reason in - mk_arrow id cx reason ~async - typeParameters (params, defaults, rest) returnType body this super + mk_arrow id cx reason ~async ~generator typeParameters + (params, defaults, rest) returnType body this super | TaggedTemplate { TaggedTemplate.tag = _, Identifier (_, @@ -3428,8 +3428,37 @@ and expression_ ~is_cond cx loc e = Ast.Expression.(match e with let reason = mk_reason name name_loc in mk_class cx loc reason c + | Yield { Yield.argument; delegate = false } -> + let reason = mk_reason "yield" loc in + let yield = Env_js.get_var cx (internal_name "yield") reason in + let t = expression cx argument in + Flow_js.flow cx (t, yield); + let next = Env_js.get_var cx (internal_name "next") reason in + OptionalT next + + | Yield { Yield.argument; delegate = true } -> + let reason = mk_reason "yield* delegate" loc in + let next = Env_js.get_var cx + (internal_name "next") + (prefix_reason "next of parent generator in " reason) in + let yield = Env_js.get_var cx + (internal_name "yield") + (prefix_reason "yield of parent generator in " reason) in + let t = expression cx argument in + + let ret = Flow_js.mk_tvar cx + (prefix_reason "return of child generator in " reason) in + + (* widen yield with the element type of the delegated-to iterable *) + let iterable = Flow_js.get_builtin_typeapp cx + (mk_reason "iteration expected on Iterable" loc) + "$Iterable" + [yield; ret; next] in + Flow_js.flow cx (t, iterable); + + ret + (* TODO *) - | Yield _ | Comprehension _ | Generator _ | Let _ -> @@ -4220,7 +4249,7 @@ and react_create_class cx loc class_props = Ast.Expression.( returnType; typeParameters; _ } = func in let reason = mk_reason "defaultProps" vloc in - let t = mk_method cx reason ~async:false (params, defaults, rest) + let t = mk_method cx reason ~async:false ~generator:false (params, defaults, rest) returnType body this (MixedT reason) in (match t with @@ -4241,7 +4270,7 @@ and react_create_class cx loc class_props = Ast.Expression.( returnType; typeParameters; _ } = func in let reason = mk_reason "initialState" vloc in - let t = mk_method cx reason ~async:false (params, defaults, rest) + let t = mk_method cx reason ~async:false ~generator:false (params, defaults, rest) returnType body this (MixedT reason) in let override_state = @@ -4261,10 +4290,10 @@ and react_create_class cx loc class_props = Ast.Expression.( _ }) -> Ast.Expression.Function.( let { params; defaults; rest; body; - returnType; typeParameters; async; _ } = func + returnType; typeParameters; async; generator; _ } = func in let reason = mk_reason "function" vloc in - let t = mk_method cx reason ~async (params, defaults, rest) + let t = mk_method cx reason ~async ~generator (params, defaults, rest) returnType body this (MixedT reason) in fmap, SMap.add name t mmap @@ -5063,6 +5092,8 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( let this, super, method_sigs, getter_sigs, setter_sigs = if static then static_info else instance_info in + let yield = MixedT (mk_reason "no yield" loc) in + let next = MixedT (mk_reason "no next" loc) in let sigs_to_use = match kind with | Method.Constructor @@ -5082,7 +5113,7 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( let ret = Flow_js.subst cx map_ ret in mk_body None cx ~async - param_types_map param_loc_map ret body this super; + param_types_map param_loc_map ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); ignore (Abnormal.swap Abnormal.Throw save_throw_exn) @@ -5433,7 +5464,7 @@ and mk_interface cx reason_i typeparams map (sfmap, smmap, fmap, mmap) extends s signature consisting of type parameters, parameter types, parameter names, and return type, check the body against that signature by adding `this` and `super` to the environment, and return the signature. *) -and function_decl id cx (reason:reason) ~async +and function_decl id cx (reason:reason) ~async ~generator type_params params ret body this super = let typeparams, type_params_map = mk_type_param_declarations cx type_params in @@ -5441,6 +5472,23 @@ and function_decl id cx (reason:reason) ~async let (params, pnames, ret, param_types_map, param_types_loc) = mk_params_ret cx type_params_map params (body_loc body, ret) in + (* If this is a generator function, the return type annotation can be an + application of the Generator type. We don't want to flow the explicit or + phantom return type into the Generator typeapp, but we still want to be + able to flow the Generator type constructed below into the annotation, so + we store off the converted annotation in _ret until then and proceed with a + tvar in its place. *) + let _ret = ret in + let (yield,ret,next) = if generator then ( + Flow_js.mk_tvar cx (prefix_reason "yield of " reason), + Flow_js.mk_tvar cx (prefix_reason "return of " reason), + Flow_js.mk_tvar cx (prefix_reason "next of " reason) + ) else ( + MixedT (replace_reason "no yield" reason), + ret, + MixedT (replace_reason "no next" reason) + ) in + let save_return_exn = Abnormal.swap Abnormal.Return false in let save_throw_exn = Abnormal.swap Abnormal.Throw false in Flow_js.generate_tests cx reason typeparams (fun map_ -> @@ -5448,7 +5496,7 @@ and function_decl id cx (reason:reason) ~async param_types_map |> SMap.map (Flow_js.subst cx map_) in let ret = Flow_js.subst cx map_ ret in - mk_body id cx ~async param_types_map param_types_loc ret body this super; + mk_body id cx ~async param_types_map param_types_loc ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); @@ -5460,6 +5508,23 @@ and function_decl id cx (reason:reason) ~async else ret in + (* If this is a generator function, we don't want to use the type from the + return statement as the return type of the function. Instead, we want to + return a Generator typeapp where the inferred return type flows into the + Generator's R type param. Since generator functions can have explicit type + annotations, flow the inferred type into the annotation as well. *) + let ret = + if generator then + let t = Flow_js.get_builtin_typeapp + cx + reason + "Generator" + [yield; ret; next] in + Flow_js.flow cx (t, _ret); + t + else ret + in + (typeparams,params,pnames,ret) and is_void cx = function @@ -5470,7 +5535,7 @@ and is_void cx = function and mk_upper_bound cx locs name t = Scope.create_entry t t (SMap.get name locs) -and mk_body id cx ~async param_types_map param_locs_map ret body this super = +and mk_body id cx ~async param_types_map param_locs_map ret body this super yield next = let ctx = Env_js.get_scopes () in let new_ctx = Env_js.clone_scopes ctx in Env_js.update_env cx new_ctx; @@ -5492,6 +5557,8 @@ and mk_body id cx ~async param_types_map param_locs_map ret body this super = (* special bindings for super, this, return value slot *) add (internal_name "super") (create_entry super super None) scope; add (internal_name "this") (create_entry this this None) scope; + add (internal_name "yield") (create_entry yield yield None) scope; + add (internal_name "next") (create_entry next next None) scope; add (internal_name "return") (create_entry ret ret None) scope; scope ) in @@ -5659,18 +5726,18 @@ and extract_type_param_instantiations = function | Some (_, typeParameters) -> typeParameters.Ast.Type.ParameterInstantiation.params (* Process a function definition, returning a (polymorphic) function type. *) -and mk_function id cx reason ~async type_params params ret body this = +and mk_function id cx reason ~async ~generator type_params params ret body this = (* Normally, functions do not have access to super. *) let super = MixedT (replace_reason "empty super object" reason) in let signature = - function_decl id cx reason ~async type_params params ret body this super + function_decl id cx reason ~async ~generator type_params params ret body this super in mk_function_type cx reason this signature (* Process an arrow function, returning a (polymorphic) function type. *) -and mk_arrow id cx reason ~async type_params params ret body this super = +and mk_arrow id cx reason ~async ~generator type_params params ret body this super = let signature = - function_decl id cx reason ~async type_params params ret body this super + function_decl id cx reason ~async ~generator type_params params ret body this super in (* Do not expose the type of `this` in the function's type. The call to function_decl above has already done the necessary checking of `this` in @@ -5703,9 +5770,9 @@ and mk_function_type cx reason this signature = (* This function is around for the sole purpose of modeling some method-like behaviors of non-ES6 React classes. It is otherwise deprecated. *) -and mk_method cx reason ~async params ret body this super = +and mk_method cx reason ~async ~generator params ret body this super = let (_,params,pnames,ret) = - function_decl None cx ~async reason None params ret body this super + function_decl None cx ~async ~generator reason None params ret body this super in FunT (reason, Flow_js.dummy_static, Flow_js.dummy_prototype, Flow_js.mk_functiontype2 diff --git a/tests/async/async.exp b/tests/async/async.exp index b09e723615c..1962b88da51 100644 --- a/tests/async/async.exp +++ b/tests/async/async.exp @@ -3,29 +3,29 @@ async.js:12:3,11: async return Error: async.js:12:10,10: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type async.js:30:30,35: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type async.js:45:22,25: undefined This type is incompatible with -[LIB] core.js:352:1,379:1: Promise +[LIB] core.js:360:1,387:1: Promise async2.js:6:3,12: async return Error: async2.js:6:10,11: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type async2.js:29:21,24: undefined This type is incompatible with -[LIB] core.js:352:1,379:1: Promise +[LIB] core.js:360:1,387:1: Promise async2.js:43:28,31: undefined This type is incompatible with -[LIB] core.js:352:1,379:1: Promise +[LIB] core.js:360:1,387:1: Promise async2.js:48:3,17: undefined This type is incompatible with @@ -35,7 +35,7 @@ async3.js:23:11,21: await Error: async3.js:12:10,11: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type await_not_in_async.js:5:9,9: Unexpected number diff --git a/tests/forof/forof.exp b/tests/forof/forof.exp index ffb52d66d1f..f58436e0688 100644 --- a/tests/forof/forof.exp +++ b/tests/forof/forof.exp @@ -13,11 +13,11 @@ This type is incompatible with forof.js:32:12,17: number This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type forof.js:39:12,17: number This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type forof.js:43:28,33: string This type is incompatible with diff --git a/tests/generators/.flowconfig b/tests/generators/.flowconfig new file mode 100644 index 00000000000..4a58bdcdef3 --- /dev/null +++ b/tests/generators/.flowconfig @@ -0,0 +1,7 @@ +[ignore] + +[include] + +[libs] + +[options] diff --git a/tests/generators/generators.exp b/tests/generators/generators.exp new file mode 100644 index 00000000000..d8ddc0dcea5 --- /dev/null +++ b/tests/generators/generators.exp @@ -0,0 +1,50 @@ + +generators.js:3:9,10: string +This type is incompatible with +generators.js:1:35,40: number + +generators.js:6:46,51: number +This type is incompatible with +generators.js:14:10,15: string + +generators.js:23:10,11: string +This type is incompatible with +generators.js:22:46,51: number + +generators.js:27:27,27: number +This type is incompatible with +generators.js:30:36,41: string + +generators.js:31:23,42: call of method next +Error: +generators.js:31:41,41: number +This type is incompatible with +generators.js:27:11,17: boolean + +generators.js:31:23,48: property value +Error: +generators.js:28:10,11: string +This type is incompatible with +generators.js:35:22,28: boolean + +generators.js:70:32,33: string +This type is incompatible with +generators.js:66:13,18: number + +generators.js:74:11,12: string +This type is incompatible with +generators.js:80:8,13: number + +generators.js:85:12,13: string +This type is incompatible with +generators.js:88:10,15: number + +generators.js:95:33,34: string +This type is incompatible with +[LIB] core.js:307:37,40: undefined + +generators.js:97:45,50: number +This type is incompatible with +generators.js:101:8,13: string + +Found 11 errors diff --git a/tests/generators/generators.js b/tests/generators/generators.js new file mode 100644 index 00000000000..a9caadb18be --- /dev/null +++ b/tests/generators/generators.js @@ -0,0 +1,106 @@ +function *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number +} + +function *stmt_next(): Generator { + var a = yield undefined; + if (a) { + (a : number); // ok + } + + var b = yield undefined; + if (b) { + (b : string); // error: number ~> string + } +} + +function *stmt_return_ok(): Generator { + return 0; // ok +} + +function *stmt_return_err(): Generator { + return ""; // error: string ~> number +} + +function *infer_stmt() { + var x: ?boolean = yield 0; + return ""; +} +for (var x of infer_stmt()) { (x : string) } // error: number ~> string +var infer_stmt_next = infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +function *widen_next() { + var x = yield 0; + if (x == null) { + } else if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} +widen_next().next(0) +widen_next().next("") +widen_next().next(true) + +function *widen_yield() { + yield 0; + yield ""; + yield true; +} +for (var x of widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +function *delegate_next_generator() { + function *inner() { + var x: ?number = yield undefined; // error: string ~> number + } + yield *inner(); +} +delegate_next_generator().next(""); + +function *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); +} +for (var x of delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +function *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner() +} + +// only generators can make use of a value passed to next +function *delegate_next_iterable(xs: Array) { + yield *xs; +} +delegate_next_iterable([]).next(""); // error: Iterator has no next value + +function *delegate_yield_iterable(xs: Array) { + yield *xs; +} +for (var x of delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} + +function *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value +} diff --git a/tests/iterable/iterable.exp b/tests/iterable/iterable.exp index 5fd5a819f24..40f84da748f 100644 --- a/tests/iterable/iterable.exp +++ b/tests/iterable/iterable.exp @@ -17,7 +17,7 @@ caching_bug.js:21:62,67: string map.js:13:55,60: string This type is incompatible with -[LIB] core.js:315:28,33: tuple type +[LIB] core.js:323:28,33: tuple type set.js:13:28,33: string This type is incompatible with diff --git a/tests/promises/promises.exp b/tests/promises/promises.exp index 3cde6f8aeb9..42c82e61793 100644 --- a/tests/promises/promises.exp +++ b/tests/promises/promises.exp @@ -3,78 +3,78 @@ promise.js:10:1,17:2: call of method then Error: promise.js:11:11,11: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:21:11,23:4: constructor call Error: promise.js:22:13,13: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:32:13,34:6: constructor call Error: promise.js:33:15,15: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:42:1,55:2: call of method then Error: promise.js:44:13,14: number This type is incompatible with -[LIB] core.js:354:25,38: union type +[LIB] core.js:362:25,38: union type promise.js:106:1,109:2: call of method then Error: promise.js:106:17,17: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:112:17,34: call of method resolve Error: promise.js:112:33,33: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:118:33,50: call of method resolve Error: promise.js:118:49,49: number This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:148:1,153:4: call of method then Error: promise.js:149:32,37: string This type is incompatible with -[LIB] core.js:359:33,46: union type +[LIB] core.js:367:33,46: union type promise.js:157:32,54: call of method resolve Error: promise.js:157:48,53: string This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:165:48,70: call of method resolve Error: promise.js:165:64,69: string This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:188:1,193:4: call of method then Error: promise.js:189:33,38: string This type is incompatible with -[LIB] core.js:369:34,48: union type +[LIB] core.js:377:34,48: union type promise.js:197:33,55: call of method resolve Error: promise.js:197:49,54: string This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type promise.js:205:49,71: call of method resolve Error: promise.js:205:65,70: string This type is incompatible with -[LIB] core.js:372:32,45: union type +[LIB] core.js:380:32,45: union type Found 13 errors diff --git a/tests/union/union.exp b/tests/union/union.exp index 00d6d6e4fbe..469722b9b06 100644 --- a/tests/union/union.exp +++ b/tests/union/union.exp @@ -3,7 +3,7 @@ issue-198.js:1:9,10:6: call of method then Error: issue-198.js:5:16,28: string This type is incompatible with -[LIB] core.js:359:33,46: union type +[LIB] core.js:367:33,46: union type issue-324.js:3:7,9: Bar This type is incompatible with From 661e9d4fdfd1a3ac41fbb233b9805403bc882db9 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 4 Sep 2015 10:34:29 -1000 Subject: [PATCH 2/7] Add comment to explicate test expectation --- tests/generators/generators.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/generators/generators.js b/tests/generators/generators.js index a9caadb18be..086f22d252d 100644 --- a/tests/generators/generators.js +++ b/tests/generators/generators.js @@ -85,7 +85,7 @@ function *delegate_return_generator() { return ""; } - var x: number = yield *inner() + var x: number = yield *inner(); // error: string ~> number } // only generators can make use of a value passed to next From 898851ae5e229b08e64f65606d66298e4344d272 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 4 Sep 2015 10:41:12 -1000 Subject: [PATCH 3/7] Change IteratorResult interface to have a single type parameter In reality, the type of IteratorResult is something like: ``` type IteratorResult = { done: false, value: Y } | { done: true, value: ?R } ``` However, boolean sentinels is not implemented. It's simpler for everyone to use the single-parameter interface, so let's keep things simple. --- lib/core.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/core.js b/lib/core.js index 9241db153b2..b2c9182aac7 100644 --- a/lib/core.js +++ b/lib/core.js @@ -295,14 +295,14 @@ declare class JSON { } /* Iterators */ -interface IteratorResult { +interface IteratorResult { done: boolean; - value?: Y|R; + value?: T; } interface $Iterator { @@iterator(): $Iterator; - next(value?: N): IteratorResult; + next(value?: N): IteratorResult; } type Iterator = $Iterator; @@ -314,7 +314,7 @@ type Iterable = $Iterable; /* Generators */ interface Generator { @@iterator(): $Iterator; - next(value?: N): IteratorResult; + next(value?: N): IteratorResult; } /* Maps and Sets */ From 595206b49776e17e8e8a257b317819ac1c5085a5 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 4 Sep 2015 23:18:39 -1000 Subject: [PATCH 4/7] Implement class method generators Class methods go through a quite different, and a bit more convoluted process. The hack I was using for function declarations where I swapped out the inferred return type with a Generator typeapp no longer applies. This is a good thing, because it forced me to replace the hack with something better. Like async functions, the fancy generator return type is established in the Return statement handler directly. If there is no explicit return, we default to a Generator typeapp where R=void. I intentionally left the capacity for async=true/generator=true var scopes. Babel, via regenerator[1], seems to understand `async function*` using the AsyncIterator[2] proposal. For now, the parser will reject async generator functions, but we might choose to support async iterators in the future, so having the capacity to do so in VarScope seems right. 1. https://github.com/facebook/regenerator/blob/9423b4ade99173b603430a151599cf8637166f16/test/async.es6.js#L259 2. https://github.com/zenparsing/async-iteration/#async-generator-functions --- src/typing/constraint_js.ml | 16 +++-- src/typing/constraint_js.mli | 4 +- src/typing/env_js.ml | 8 ++- src/typing/env_js.mli | 1 + src/typing/type_inference_js.ml | 77 ++++++++++----------- tests/generators/async.js | 4 ++ tests/generators/class.js | 118 ++++++++++++++++++++++++++++++++ tests/generators/generators.exp | 64 ++++++++++++++++- 8 files changed, 245 insertions(+), 47 deletions(-) create mode 100644 tests/generators/async.js create mode 100644 tests/generators/class.js diff --git a/src/typing/constraint_js.ml b/src/typing/constraint_js.ml index e7b141ff1ae..d669201106c 100644 --- a/src/typing/constraint_js.ml +++ b/src/typing/constraint_js.ml @@ -806,7 +806,8 @@ module Scope = struct (* a var scope corresponds to a runtime activation, e.g. a function. *) type var_scope_attrs = { - async: bool + async: bool; + generator: bool } (* var and lexical scopes differ in hoisting behavior @@ -837,10 +838,16 @@ module Scope = struct } (* return a fresh scope of the most common kind (var, non-async) *) - let fresh () = fresh_impl (VarScope { async = false }) + let fresh () = + fresh_impl (VarScope { async = false; generator = false }) (* return a fresh async var scope *) - let fresh_async () = fresh_impl (VarScope { async = true }) + let fresh_async () = + fresh_impl (VarScope { async = true; generator = false }) + + (* return a fresh generator var scope *) + let fresh_generator () = + fresh_impl (VarScope { async = false; generator = true }) (* return a fresh lexical scope *) let fresh_lex () = fresh_impl LexScope @@ -2520,7 +2527,8 @@ let string_of_scope = Scope.( in let string_of_scope_kind = function - | VarScope attrs -> spf "VarScope { async: %b }" attrs.async + | VarScope { async; generator } -> + spf "VarScope { async: %b; generator: %b }" async generator | LexScope -> "Lex" in diff --git a/src/typing/constraint_js.mli b/src/typing/constraint_js.mli index 615e6f6f581..f7ba5e18210 100644 --- a/src/typing/constraint_js.mli +++ b/src/typing/constraint_js.mli @@ -342,7 +342,8 @@ module Scope: sig = Key.t type var_scope_attrs = { - async: bool + async: bool; + generator: bool } type kind = @@ -363,6 +364,7 @@ module Scope: sig val fresh: unit -> t val fresh_async: unit -> t + val fresh_generator: unit -> t val fresh_lex: unit -> t val clone: t -> t diff --git a/src/typing/env_js.ml b/src/typing/env_js.ml index 2e6c5bee46d..c97adfc3a7c 100644 --- a/src/typing/env_js.ml +++ b/src/typing/env_js.ml @@ -97,7 +97,13 @@ let clone_scopes scopes = (* TODO will need to walk once LexScopes appear *) let in_async_scope () = Scope.( match (peek_scope ()).kind with - | VarScope { async } -> async + | VarScope { async; _ } -> async + | _ -> false +) + +let in_generator_scope () = Scope.( + match (peek_scope ()).kind with + | VarScope { generator; _ } -> generator | _ -> false ) diff --git a/src/typing/env_js.mli b/src/typing/env_js.mli index 078751feb9d..576f702e031 100644 --- a/src/typing/env_js.mli +++ b/src/typing/env_js.mli @@ -20,6 +20,7 @@ val get_scopes: unit -> Scope.t list val clone_scopes: Scope.t list -> Scope.t list val in_async_scope: unit -> bool +val in_generator_scope: unit -> bool val all_entries: unit -> Entry.t SMap.t diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 4f4bbaf063b..3af17cf80fe 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -1599,18 +1599,26 @@ and statement cx type_params_map = Ast.Statement.( | None -> void_ loc | Some expr -> expression cx type_params_map expr in - (* if we're in an async function, convert the return - expression's type T to Promise *) - let t = if Env_js.in_async_scope () - then - (* Unwrap and wrap t with a Promise by returning whatever - Promise.resolve(t) returns. *) + let t = + if Env_js.in_async_scope () then + (* Convert the return expression's type T to Promise. If the + * expression type is itself a Promise, ensure we still return + * a Promise via Promise.resolve. *) let reason = mk_reason "async return" loc in let promise = Env_js.get_var cx "Promise" reason in Flow_js.mk_tvar_where cx reason (fun tvar -> let call = Flow_js.mk_methodtype promise [t] tvar in Flow_js.flow cx (promise, MethodT (reason, "resolve", call)) ) + else if Env_js.in_generator_scope () then + (* Convert the return expression's type R to Generator, where + * Y and R are internals, installed earlier. *) + let reason = mk_reason "generator return" loc in + Flow_js.get_builtin_typeapp cx reason "Generator" [ + Env_js.get_var cx (internal_name "yield") reason; + t; + Env_js.get_var cx (internal_name "next") reason + ] else t in Flow_js.flow cx (t, ret); @@ -5105,7 +5113,7 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( Method.key = Ast.Expression.Object.Property.Identifier (_, { Ast.Identifier.name; _ }); value = _, { Ast.Expression.Function.params; defaults; rest; - returnType; typeParameters; body; async; _ }; + returnType; typeParameters; body; async; generator; _ }; static; kind; decorators = _; @@ -5113,8 +5121,6 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( let this, super, method_sigs, getter_sigs, setter_sigs = if static then static_info else instance_info in - let yield = MixedT (mk_reason "no yield" loc) in - let next = MixedT (mk_reason "no next" loc) in let sigs_to_use = match kind with | Method.Constructor @@ -5126,6 +5132,14 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( (_, _, ret, param_types_map, param_loc_map) = SMap.find_unsafe name sigs_to_use in + let yield, next = if generator then ( + Flow_js.mk_tvar cx (prefix_reason "yield of " reason), + Flow_js.mk_tvar cx (prefix_reason "next of " reason) + ) else ( + MixedT (replace_reason "no yield" reason), + MixedT (replace_reason "no next" reason) + ) in + let save_return_exn = Abnormal.swap Abnormal.Return false in let save_throw_exn = Abnormal.swap Abnormal.Throw false in Flow_js.generate_tests cx reason typeparams (fun map_ -> @@ -5139,7 +5153,7 @@ and mk_class_elements cx instance_info static_info body = Ast.Class.( | MixedT _ -> false | _ -> name = "constructor" in - mk_body None cx type_params_map ~async ~derived_ctor + mk_body None cx type_params_map ~async ~generator ~derived_ctor param_types_map param_loc_map ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); @@ -5502,20 +5516,11 @@ and function_decl id cx type_params_map (reason:reason) ~async ~generator let (params, pnames, ret, param_types_map, param_types_loc) = mk_params_ret cx type_params_map params (body, ret) in - (* If this is a generator function, the return type annotation can be an - application of the Generator type. We don't want to flow the explicit or - phantom return type into the Generator typeapp, but we still want to be - able to flow the Generator type constructed below into the annotation, so - we store off the converted annotation in _ret until then and proceed with a - tvar in its place. *) - let _ret = ret in - let (yield,ret,next) = if generator then ( + let yield, next = if generator then ( Flow_js.mk_tvar cx (prefix_reason "yield of " reason), - Flow_js.mk_tvar cx (prefix_reason "return of " reason), Flow_js.mk_tvar cx (prefix_reason "next of " reason) ) else ( MixedT (replace_reason "no yield" reason), - ret, MixedT (replace_reason "no next" reason) ) in @@ -5528,7 +5533,8 @@ and function_decl id cx type_params_map (reason:reason) ~async ~generator param_types_map |> SMap.map (Flow_js.subst cx map_) in let ret = Flow_js.subst cx map_ ret in - mk_body id cx type_params_map ~async param_types_map param_types_loc ret body this super yield next; + mk_body id cx type_params_map ~async ~generator + param_types_map param_types_loc ret body this super yield next; ); ignore (Abnormal.swap Abnormal.Return save_return_exn); @@ -5540,23 +5546,6 @@ and function_decl id cx type_params_map (reason:reason) ~async ~generator else ret in - (* If this is a generator function, we don't want to use the type from the - return statement as the return type of the function. Instead, we want to - return a Generator typeapp where the inferred return type flows into the - Generator's R type param. Since generator functions can have explicit type - annotations, flow the inferred type into the annotation as well. *) - let ret = - if generator then - let t = Flow_js.get_builtin_typeapp - cx - reason - "Generator" - [yield; ret; next] in - Flow_js.flow cx (t, _ret); - t - else ret - in - (typeparams,params,pnames,ret) and is_void cx = function @@ -5597,7 +5586,7 @@ and define_internal cx reason x = let opt = Env_js.get_var_declared_type cx ix reason in Env_js.set_var cx ix (Flow_js.filter_optional cx reason opt) reason -and mk_body id cx type_params_map ~async ?(derived_ctor=false) +and mk_body id cx type_params_map ~async ~generator ?(derived_ctor=false) param_types_map param_locs_map ret body this super yield next = let ctx = Env_js.get_scopes () in let new_ctx = Env_js.clone_scopes ctx in @@ -5606,7 +5595,11 @@ and mk_body id cx type_params_map ~async ?(derived_ctor=false) (* create and prepopulate function scope *) let function_scope = - let scope = Scope.(if async then fresh_async else fresh) () in + let scope = Scope.( + if async then fresh_async + else if generator then fresh_generator + else fresh + ) () in (* add param bindings *) param_types_map |> SMap.iter (fun name t -> let entry = Scope.Entry.(match SMap.get name param_locs_map with @@ -5651,6 +5644,10 @@ and mk_body id cx type_params_map ~async ?(derived_ctor=false) let reason = mk_reason "return Promise" loc in let promise = Env_js.var_ref ~lookup_mode:ForType cx "Promise" reason in TypeAppT (promise, [VoidT.at loc]) + else if generator then + let reason = mk_reason "return Generator" loc in + let ret = VoidT.at loc in + Flow_js.get_builtin_typeapp cx reason "Generator" [yield; ret; next] else VoidT (mk_reason "return undefined" loc) in diff --git a/tests/generators/async.js b/tests/generators/async.js new file mode 100644 index 00000000000..21bfdb04572 --- /dev/null +++ b/tests/generators/async.js @@ -0,0 +1,4 @@ +/* TODO: Support async iteration + * See: https://github.com/zenparsing/async-iteration/#async-generator-functions + */ +async function *unsupported() {} diff --git a/tests/generators/class.js b/tests/generators/class.js new file mode 100644 index 00000000000..8360b9cc8df --- /dev/null +++ b/tests/generators/class.js @@ -0,0 +1,118 @@ +class GeneratorExamples { + *stmt_yield(): Generator { + yield 0; // ok + yield ""; // error: string ~> number + } + + *stmt_next(): Generator { + var a = yield undefined; + if (a) { + (a : number); // ok + } + + var b = yield undefined; + if (b) { + (b : string); // error: number ~> string + } + } + + *stmt_return_ok(): Generator { + return 0; // ok + } + + *stmt_return_err(): Generator { + return ""; // error: string ~> number + } + + *infer_stmt() { + var x: ?boolean = yield 0; + return ""; + } + + *widen_next() { + var x = yield 0; + if (x == null) { + } else if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } + } + + *widen_yield() { + yield 0; + yield ""; + yield true; + } + + *delegate_next_generator() { + function *inner() { + var x: ?number = yield undefined; // error: string ~> number + } + yield *inner(); + } + + *delegate_yield_generator() { + function *inner() { + yield ""; + } + + yield *inner(); + } + + *delegate_return_generator() { + function *inner() { + return ""; + } + + var x: number = yield *inner(); // error: string ~> number + } + + // only generators can make use of a value passed to next + *delegate_next_iterable(xs: Array) { + yield *xs; + } + + *delegate_yield_iterable(xs: Array) { + yield *xs; + } + + *delegate_return_iterable(xs: Array) { + var x: void = yield *xs // ok: Iterator has no yield value + } +} + +var examples = new GeneratorExamples(); + +for (var x of examples.infer_stmt()) { (x : string) } // error: number ~> string + +var infer_stmt_next = examples.infer_stmt().next(0).value; // error: number ~> boolean +if (typeof infer_stmt_next === "undefined") { +} else if (typeof infer_stmt_next === "number") { +} else { + (infer_stmt_next : boolean) // error: string ~> boolean +} + +examples.widen_next().next(0) +examples.widen_next().next("") +examples.widen_next().next(true) + +for (var x of examples.widen_yield()) { + if (typeof x === "number") { + } else if (typeof x === "boolean") { + } else { + (x : string) // ok, sherlock + } +} + +examples.delegate_next_generator().next(""); + +for (var x of examples.delegate_yield_generator()) { + (x : number) // error: string ~> number +} + +examples.delegate_next_iterable([]).next(""); // error: Iterator has no next value + +for (var x of examples.delegate_yield_iterable([])) { + (x : string) // error: number ~> string +} diff --git a/tests/generators/generators.exp b/tests/generators/generators.exp index 03f0b3fd39e..289b0e277f9 100644 --- a/tests/generators/generators.exp +++ b/tests/generators/generators.exp @@ -1,4 +1,60 @@ +async.js:4:17,27: A function may not be both async and a generator + +class.js:4:11,12: string +This type is incompatible with +class.js:2:28,33: number + +class.js:7:39,44: number +This type is incompatible with +class.js:15:12,17: string + +class.js:23:39,44: number +This type is incompatible with +class.js:24:12,13: string + +class.js:24:12,13: string +This type is incompatible with +class.js:23:39,44: number + +class.js:28:29,29: number +This type is incompatible with +class.js:87:45,50: string + +class.js:57:13,14: string +This type is incompatible with +class.js:111:8,13: number + +class.js:65:14,15: string +This type is incompatible with +class.js:68:12,17: number + +class.js:76:38,43: number +This type is incompatible with +class.js:117:8,13: string + +class.js:89:23,51: call of method next +Error: +class.js:89:50,50: number +This type is incompatible with +class.js:28:13,19: boolean + +class.js:89:23,57: property `value` +Error: +class.js:29:12,13: string +This type is incompatible with +class.js:93:22,28: boolean + +class.js:108:41,42: string +This type is incompatible with +class.js:50:15,20: number + +class.js:114:1,44: call of method next +Error: +class.js:114:42,43: string +This type is incompatible with +[LIB] core.js:307:37,40: undefined + generators.js:3:9,10: string This type is incompatible with generators.js:1:35,40: number @@ -7,6 +63,10 @@ generators.js:6:46,51: number This type is incompatible with generators.js:14:10,15: string +generators.js:22:46,51: number +This type is incompatible with +generators.js:23:10,11: string + generators.js:23:10,11: string This type is incompatible with generators.js:22:46,51: number @@ -39,6 +99,8 @@ generators.js:85:12,13: string This type is incompatible with generators.js:88:10,15: number +generators.js:95:1,35: call of method next +Error: generators.js:95:33,34: string This type is incompatible with [LIB] core.js:307:37,40: undefined @@ -47,4 +109,4 @@ generators.js:97:45,50: number This type is incompatible with generators.js:101:8,13: string -Found 11 errors +Found 25 errors From a25f86742acb05527befd39dbdb908e1ca754aa4 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Fri, 4 Sep 2015 23:52:53 -1000 Subject: [PATCH 5/7] Arrow functions can not be generators --- src/typing/type_inference_js.ml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 3af17cf80fe..980494c665a 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -3409,7 +3409,6 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with params; defaults; rest; body; async; - generator; returnType; typeParameters; _ @@ -3418,7 +3417,7 @@ and expression_ ~is_cond cx type_params_map loc e = Ast.Expression.(match e with let reason = mk_reason desc loc in let this = this_ cx reason in let super = super_ cx reason in - mk_arrow id cx type_params_map reason ~async ~generator + mk_arrow id cx type_params_map reason ~async ~generator:false typeParameters (params, defaults, rest) returnType body this super | TaggedTemplate { From ad4b8d9ed8b6a7d24121137e53c77564701d690e Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Sat, 5 Sep 2015 00:05:49 -1000 Subject: [PATCH 6/7] Consolidate fresh, fresh_async, fresh_generator This one comes down to personal opinion I suppose. I don't feel strongly about it. --- src/typing/constraint_js.ml | 15 ++++----------- src/typing/constraint_js.mli | 4 +--- src/typing/type_inference_js.ml | 6 +----- 3 files changed, 6 insertions(+), 19 deletions(-) diff --git a/src/typing/constraint_js.ml b/src/typing/constraint_js.ml index d669201106c..c33295057ba 100644 --- a/src/typing/constraint_js.ml +++ b/src/typing/constraint_js.ml @@ -837,17 +837,10 @@ module Scope = struct refis = KeyMap.empty; } - (* return a fresh scope of the most common kind (var, non-async) *) - let fresh () = - fresh_impl (VarScope { async = false; generator = false }) - - (* return a fresh async var scope *) - let fresh_async () = - fresh_impl (VarScope { async = true; generator = false }) - - (* return a fresh generator var scope *) - let fresh_generator () = - fresh_impl (VarScope { async = false; generator = true }) + (* return a fresh scope of the most common kind (var) *) + let fresh ?(async=false) ?(generator=false) () = + assert (not (async && generator)); + fresh_impl (VarScope { async; generator }) (* return a fresh lexical scope *) let fresh_lex () = fresh_impl LexScope diff --git a/src/typing/constraint_js.mli b/src/typing/constraint_js.mli index f7ba5e18210..39a4b3c1541 100644 --- a/src/typing/constraint_js.mli +++ b/src/typing/constraint_js.mli @@ -362,9 +362,7 @@ module Scope: sig mutable refis: refi_binding KeyMap.t } - val fresh: unit -> t - val fresh_async: unit -> t - val fresh_generator: unit -> t + val fresh: ?async:bool -> ?generator:bool -> unit -> t val fresh_lex: unit -> t val clone: t -> t diff --git a/src/typing/type_inference_js.ml b/src/typing/type_inference_js.ml index 980494c665a..c4ac508ed5d 100644 --- a/src/typing/type_inference_js.ml +++ b/src/typing/type_inference_js.ml @@ -5594,11 +5594,7 @@ and mk_body id cx type_params_map ~async ~generator ?(derived_ctor=false) (* create and prepopulate function scope *) let function_scope = - let scope = Scope.( - if async then fresh_async - else if generator then fresh_generator - else fresh - ) () in + let scope = Scope.fresh ~async ~generator () in (* add param bindings *) param_types_map |> SMap.iter (fun name t -> let entry = Scope.Entry.(match SMap.get name param_locs_map with From 17a9d52d798665a097bc2dd7561ced11b8043fb9 Mon Sep 17 00:00:00 2001 From: Sam Goldman Date: Sat, 5 Sep 2015 00:10:29 -1000 Subject: [PATCH 7/7] Y,R,N = Yield,Return,Next --- lib/core.js | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/core.js b/lib/core.js index b2c9182aac7..c4e7afe76c3 100644 --- a/lib/core.js +++ b/lib/core.js @@ -300,21 +300,21 @@ interface IteratorResult { value?: T; } -interface $Iterator { - @@iterator(): $Iterator; - next(value?: N): IteratorResult; +interface $Iterator { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; } type Iterator = $Iterator; -interface $Iterable { - @@iterator(): $Iterator; +interface $Iterable { + @@iterator(): $Iterator; } type Iterable = $Iterable; /* Generators */ -interface Generator { - @@iterator(): $Iterator; - next(value?: N): IteratorResult; +interface Generator { + @@iterator(): $Iterator; + next(value?: Next): IteratorResult; } /* Maps and Sets */