diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 448bf927751..b8041f89287 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -954,7 +954,7 @@ namespace ts { addAntecedent(preLoopLabel, currentFlow); currentFlow = preLoopLabel; if (node.kind === SyntaxKind.ForOfStatement) { - bind(node.awaitKeyword); + bind(node.modifierToken); } bind(node.expression); addAntecedent(postLoopLabel, currentFlow); @@ -3125,9 +3125,13 @@ namespace ts { break; case SyntaxKind.ForOfStatement: - // for-of might be ESNext if it has a rest destructuring - transformFlags |= TransformFlags.AssertESNext; - // FALLTHROUGH + if ((node).modifierToken) { + transformFlags |= TransformFlags.AssertES2017; + } + + transformFlags |= TransformFlags.AssertES2015; + break; + case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.TemplateHead: case SyntaxKind.TemplateMiddle: @@ -3142,7 +3146,17 @@ namespace ts { case SyntaxKind.ForOfStatement: // This node is either ES2015 syntax or ES2017 syntax (if it is a for-await-of). - transformFlags |= (node).awaitKeyword ? TransformFlags.AssertES2017 : TransformFlags.AssertES2015; + switch (getForOfModifierKind(node)) { + case SyntaxKind.AwaitKeyword: + transformFlags |= TransformFlags.AssertES2017; + break; + case SyntaxKind.EachKeyword: + transformFlags |= TransformFlags.AssertTypeScript; + break; + default: + transformFlags |= TransformFlags.AssertES2015; + break; + } break; case SyntaxKind.YieldExpression: diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index d6f7e231d83..98260343fbd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -189,6 +189,13 @@ namespace ts { let globalNumberType: ObjectType; let globalBooleanType: ObjectType; let globalRegExpType: ObjectType; + let globalESSymbolType: ObjectType; + let globalIterableType: GenericType; + let globalIteratorType: GenericType; + let globalIterableIteratorType: GenericType; + let globalAsyncIterableType: GenericType; + let globalAsyncIteratorType: GenericType; + let globalAsyncIterableIteratorType: GenericType; let anyArrayType: Type; let autoArrayType: Type; let anyReadonlyArrayType: Type; @@ -198,14 +205,6 @@ namespace ts { // and they will not get an error from not having unrelated library files let getGlobalTemplateStringsArrayType: () => ObjectType; - let getGlobalESSymbolType: () => ObjectType; - let getGlobalIterableType: () => GenericType; - let getGlobalIteratorType: () => GenericType; - let getGlobalIterableIteratorType: () => GenericType; - let getGlobalAsyncIterableType: () => GenericType; - let getGlobalAsyncIteratorType: () => GenericType; - let getGlobalAsyncIterableIteratorType: () => GenericType; - let getGlobalTypedPropertyDescriptorType: () => GenericType; let getGlobalPromiseType: () => GenericType; let tryGetGlobalPromiseType: () => GenericType; @@ -3213,8 +3212,8 @@ namespace ts { // missing properties/signatures required to get its iteratedType (like // [Symbol.iterator] or next). This may be because we accessed properties from anyType, // or it may have led to an error inside getElementTypeOfIterable. - const isForAwaitOf = (declaration.parent.parent).awaitKeyword !== undefined; - return checkRightHandSideOfForOf((declaration.parent.parent).expression, isForAwaitOf) || anyType; + const forOfStatement = declaration.parent.parent; + return checkRightHandSideOfForOf(forOfStatement.expression, getForOfModifierKind(forOfStatement)) || anyType; } if (isBindingPattern(declaration.parent)) { @@ -3631,7 +3630,9 @@ namespace ts { } function isReferenceToType(type: Type, target: Type) { - return (getObjectFlags(type) & ObjectFlags.Reference) !== 0 + return type !== undefined + && target !== undefined + && (getObjectFlags(type) & ObjectFlags.Reference) !== 0 && (type).target === target; } @@ -4668,7 +4669,7 @@ namespace ts { return t.flags & TypeFlags.StringLike ? globalStringType : t.flags & TypeFlags.NumberLike ? globalNumberType : t.flags & TypeFlags.BooleanLike ? globalBooleanType : - t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType() : + t.flags & TypeFlags.ESSymbol ? getGlobalESSymbolType(/*checked*/ languageVersion >= ScriptTarget.ES2015) : t.flags & TypeFlags.Index ? stringOrNumberType : t; } @@ -5499,22 +5500,51 @@ namespace ts { return type; } - function getGlobalValueSymbol(name: string): Symbol { - return getGlobalSymbol(name, SymbolFlags.Value, Diagnostics.Cannot_find_global_value_0); + function getGlobalValueSymbol(name: string, unchecked?: boolean): Symbol { + return getGlobalSymbol(name, SymbolFlags.Value, unchecked ? undefined : Diagnostics.Cannot_find_global_value_0); } - function getGlobalTypeSymbol(name: string): Symbol { - return getGlobalSymbol(name, SymbolFlags.Type, Diagnostics.Cannot_find_global_type_0); + function getGlobalTypeSymbol(name: string, unchecked?: boolean): Symbol { + return getGlobalSymbol(name, SymbolFlags.Type, unchecked ? undefined : Diagnostics.Cannot_find_global_type_0); } function getGlobalSymbol(name: string, meaning: SymbolFlags, diagnostic: DiagnosticMessage): Symbol { return resolveName(undefined, name, meaning, diagnostic, name); } - function getGlobalType(name: string, arity?: 0): ObjectType; - function getGlobalType(name: string, arity: number): GenericType; - function getGlobalType(name: string, arity = 0): ObjectType { - return getTypeOfGlobalSymbol(getGlobalTypeSymbol(name), arity); + function getGlobalType(name: string, arity?: 0, unchecked?: boolean): ObjectType; + function getGlobalType(name: string, arity: number, unchecked?: boolean): GenericType; + function getGlobalType(name: string, arity = 0, unchecked?: boolean): ObjectType { + const symbol = getGlobalTypeSymbol(name, unchecked); + return symbol || !unchecked ? getTypeOfGlobalSymbol(symbol, arity) : undefined; + } + + function getGlobalESSymbolType(checked: boolean) { + return globalESSymbolType || (globalESSymbolType = getGlobalType("Symbol", 0, !checked)) || emptyObjectType; + } + + function getGlobalAsyncIterableType(checked: boolean) { + return globalAsyncIterableType || (globalAsyncIterableType = getGlobalType("AsyncIterable", 1, !checked)) || emptyGenericType; + } + + function getGlobalAsyncIteratorType(checked: boolean) { + return globalAsyncIteratorType || (globalAsyncIteratorType = getGlobalType("AsyncIterator", 1, !checked)) || emptyGenericType; + } + + function getGlobalAsyncIterableIteratorType(checked: boolean) { + return globalAsyncIterableIteratorType || (globalAsyncIterableIteratorType = getGlobalType("AsyncIterableIterator", 1, !checked)) || emptyGenericType; + } + + function getGlobalIterableType(checked: boolean) { + return globalIterableType || (globalIterableType = getGlobalType("Iterable", 1, !checked)) || emptyGenericType; + } + + function getGlobalIteratorType(checked: boolean) { + return globalIteratorType || (globalIteratorType = getGlobalType("Iterator", 1, !checked)) || emptyGenericType; + } + + function getGlobalIterableIteratorType(checked: boolean) { + return globalIterableIteratorType || (globalIterableIteratorType = getGlobalType("IterableIterator", 1, !checked)) || emptyGenericType; } /** @@ -5545,19 +5575,19 @@ namespace ts { } function createAsyncIterableType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalAsyncIterableType(), [iteratedType]); + return createTypeFromGenericGlobalType(getGlobalAsyncIterableType(/*checked*/ true), [iteratedType]); } function createAsyncIterableIteratorType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalAsyncIterableIteratorType(), [iteratedType]); + return createTypeFromGenericGlobalType(getGlobalAsyncIterableIteratorType(/*checked*/ true), [iteratedType]); } function createIterableType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalIterableType(), [iteratedType]); + return createTypeFromGenericGlobalType(getGlobalIterableType(/*checked*/ true), [iteratedType]); } function createIterableIteratorType(iteratedType: Type): Type { - return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(), [iteratedType]); + return createTypeFromGenericGlobalType(getGlobalIterableIteratorType(/*checked*/ true), [iteratedType]); } function createArrayType(elementType: Type): ObjectType { @@ -7061,7 +7091,7 @@ namespace ts { if ((globalStringType === source && stringType === target) || (globalNumberType === source && numberType === target) || (globalBooleanType === source && booleanType === target) || - (getGlobalESSymbolType() === source && esSymbolType === target)) { + (getGlobalESSymbolType(/*checked*/ false) === source && esSymbolType === target)) { reportError(Diagnostics._0_is_a_primitive_but_1_is_a_wrapper_object_Prefer_using_0_when_possible, targetType, sourceType); } } @@ -8985,7 +9015,7 @@ namespace ts { case SyntaxKind.ForInStatement: return stringType; case SyntaxKind.ForOfStatement: - return checkRightHandSideOfForOf((parent).expression, (parent).awaitKeyword !== undefined) || unknownType; + return checkRightHandSideOfForOf((parent).expression, getForOfModifierKind(parent)) || unknownType; case SyntaxKind.BinaryExpression: return getAssignedTypeOfBinaryExpression(parent); case SyntaxKind.DeleteExpression: @@ -9029,8 +9059,7 @@ namespace ts { return stringType; } if (node.parent.parent.kind === SyntaxKind.ForOfStatement) { - const isForAwaitOf = (node.parent.parent).awaitKeyword !== undefined; - return checkRightHandSideOfForOf((node.parent.parent).expression, isForAwaitOf) || unknownType; + return checkRightHandSideOfForOf((node.parent.parent).expression, getForOfModifierKind(node.parent.parent)) || unknownType; } return unknownType; } @@ -10838,7 +10867,7 @@ namespace ts { const index = indexOf(arrayLiteral.elements, node); return getTypeOfPropertyOfContextualType(type, "" + index) || getIndexTypeOfContextualType(type, IndexKind.Number) - || getIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*checkAssignability*/ false); + || getIteratedTypeOrElementType(type, /*errorNode*/ undefined, /*allowStringInput*/ false, /*checkAssignability*/ false, /*arrayLikeOnly*/ false); } return undefined; } @@ -11091,7 +11120,7 @@ namespace ts { // if there is no index type / iterated type. const restArrayType = checkExpression((e).expression, contextualMapper); const restElementType = getIndexTypeOfType(restArrayType, IndexKind.Number) || - getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*checkAssignability*/ false); + getIteratedTypeOrElementType(restArrayType, /*errorNode*/ undefined, /*allowStringInput*/ false, /*checkAssignability*/ false, /*arrayLikeOnly*/ false); if (restElementType) { elementTypes.push(restElementType); } @@ -15207,8 +15236,12 @@ namespace ts { error(node.type, Diagnostics.A_generator_cannot_have_a_void_type_annotation); } else { - const generatorElementType = getIteratedTypeOfIterableIterator(returnType) || anyType; - const iterableIteratorInstantiation = createIterableIteratorType(generatorElementType); + const generatorElementType = functionFlags & FunctionFlags.Async + ? getIteratedTypeOfAsyncIterableIterator(returnType) || anyType // AsyncGenerator function + : getIteratedTypeOfIterableIterator(returnType) || anyType; // Generator function + const iterableIteratorInstantiation = functionFlags & FunctionFlags.Async + ? createAsyncIterableIteratorType(generatorElementType) // AsyncGenerator function + : createIterableIteratorType(generatorElementType); // Generator function // Naively, one could check that IterableIterator is assignable to the return type annotation. // However, that would not catch the error in the following case. @@ -17108,7 +17141,7 @@ namespace ts { checkGrammarForInOrForOfStatement(node); if (node.kind === SyntaxKind.ForOfStatement) { - if ((node).awaitKeyword) { + if (getForOfModifierKind(node) === SyntaxKind.AwaitKeyword) { if (languageVersion < ScriptTarget.ES2017) { checkExternalEmitHelpers(node, ExternalEmitHelpers.ForAwaitOfIncludes); } @@ -17128,7 +17161,7 @@ namespace ts { } else { const varExpr = node.initializer; - const iteratedType = checkRightHandSideOfForOf(node.expression, node.awaitKeyword !== undefined); + const iteratedType = checkRightHandSideOfForOf(node.expression, getForOfModifierKind(node)); // There may be a destructuring assignment on the left side if (varExpr.kind === SyntaxKind.ArrayLiteralExpression || varExpr.kind === SyntaxKind.ObjectLiteralExpression) { @@ -17215,28 +17248,28 @@ namespace ts { } } - function checkRightHandSideOfForOf(rhsExpression: Expression, isForAwaitOf: boolean): Type { + function checkRightHandSideOfForOf(rhsExpression: Expression, modifierKind: SyntaxKind): Type { const expressionType = checkNonNullExpression(rhsExpression); - return isForAwaitOf + return modifierKind === SyntaxKind.AwaitKeyword ? checkIteratedTypeOfIterableOrAsyncIterable(expressionType, rhsExpression) - : checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true); + : checkIteratedTypeOrElementType(expressionType, rhsExpression, /*allowStringInput*/ true, /*arrayLikeOnly*/ modifierKind === SyntaxKind.EachKeyword); } - function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean): Type { + function checkIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, arrayLikeOnly?: boolean): Type { if (isTypeAny(inputType)) { return inputType; } - return getIteratedTypeOrElementType(inputType, errorNode, allowStringInput, /*checkAssignability*/ true) || anyType; + return getIteratedTypeOrElementType(inputType, errorNode, allowStringInput, /*checkAssignability*/ true, arrayLikeOnly) || anyType; } /** * When consuming an iterable type in a for..of, spread, or iterator destructuring assignment * we want to get the iterated type of an iterable for ES2015 or later, or the iterated type - * of a pseudo-iterable or element type of an array like for ES2015 or earlier. + * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier. */ - function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, checkAssignability: boolean): Type { - if (languageVersion >= ScriptTarget.ES2015) { + function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, checkAssignability: boolean, arrayLikeOnly: boolean): Type { + if (!arrayLikeOnly && languageVersion >= ScriptTarget.ES2015) { const iteratedType = getIteratedTypeOfIterable(inputType, errorNode); if (checkAssignability && errorNode && iteratedType) { checkTypeAssignableTo(inputType, createIterableType(iteratedType), errorNode); @@ -17244,8 +17277,8 @@ namespace ts { return iteratedType; } return allowStringInput - ? getIteratedTypeOfPseudoIterableOrElementTypeOfArrayOrString(inputType, errorNode, checkAssignability) - : getIteratedTypeOfPseudoIterableOrElementTypeOfArray(inputType, errorNode, checkAssignability); + ? getIteratedTypeOfIterableOrElementTypeOfArrayOrString(inputType, errorNode, checkAssignability, arrayLikeOnly) + : getIteratedTypeOfIterableOrElementTypeOfArray(inputType, errorNode, checkAssignability, arrayLikeOnly); } function checkIteratedTypeOfIterableOrAsyncIterable(asyncIterable: Type, errorNode: Node): Type { @@ -17299,14 +17332,14 @@ namespace ts { // As an optimization, if the type is instantiated directly using the // globalAsyncIterableType (AsyncIterable) or globalAsyncIterableIteratorType // (AsyncIterableIterator), then just grab its type argument. - if (isReferenceToType(type, getGlobalAsyncIterableType()) || - isReferenceToType(type, getGlobalAsyncIterableIteratorType())) { + if (isReferenceToType(type, getGlobalAsyncIterableType(/*checked*/ false)) || + isReferenceToType(type, getGlobalAsyncIterableIteratorType(/*checked*/ false))) { return typeAsAsyncIterable.iteratedTypeOfAsyncIterable = (type).typeArguments[0]; } if (allowIterables) { - if (isReferenceToType(type, getGlobalIterableType()) || - isReferenceToType(type, getGlobalIterableIteratorType())) { + if (isReferenceToType(type, getGlobalIterableType(/*checked*/ false)) || + isReferenceToType(type, getGlobalIterableIteratorType(/*checked*/ false))) { return typeAsAsyncIterable.iteratedTypeOfAsyncIterable = (type).typeArguments[0]; } } @@ -17359,7 +17392,7 @@ namespace ts { // As an optimization, if the type is instantiated directly using the // globalAsyncIteratorType (AsyncIterator), then just grab its type argument. - if (isReferenceToType(type, getGlobalAsyncIteratorType())) { + if (isReferenceToType(type, getGlobalAsyncIteratorType(/*checked*/ false))) { return typeAsAsyncIterator.iteratedTypeOfAsyncIterator = (type).typeArguments[0]; } @@ -17439,15 +17472,12 @@ namespace ts { // As an optimization, if the type is instantiated directly using the // globalIterableType (Iterable) or globalIterableIteratorType (IterableIterator), // then just grab its type argument. - if (isReferenceToType(type, getGlobalIterableType()) || - isReferenceToType(type, getGlobalIterableIteratorType())) { + if (isReferenceToType(type, getGlobalIterableType(/*checked*/ false)) || + isReferenceToType(type, getGlobalIterableIteratorType(/*checked*/ false))) { return typeAsIterable.iteratedTypeOfIterable = (type).typeArguments[0]; } - const propertyName = languageVersion >= ScriptTarget.ES2015 - ? getPropertyNameForKnownSymbolName("iterator") - : "___iterator__"; - + const propertyName = getPropertyNameForKnownSymbolName("iterator"); const iteratorMethod = getTypeOfPropertyOfType(type, propertyName); if (isTypeAny(iteratorMethod)) { return undefined; @@ -17489,7 +17519,7 @@ namespace ts { // As an optimization, if the type is instantiated directly using the globalIteratorType (Iterator), // then just grab its type argument. - if (isReferenceToType(type, getGlobalIteratorType())) { + if (isReferenceToType(type, getGlobalIteratorType(/*checked*/ false))) { return typeAsIterator.iteratedTypeOfIterator = (type).typeArguments[0]; } @@ -17523,10 +17553,9 @@ namespace ts { } /** - * A generator may have a return type of Iterator, Iterable (PseudoIterable in - * ES5), or IterableIterator (PseudoIterableIterator in ES5). This function can be - * used to extract the iterated type from this return type for contextual typing and - * verifying signatures. + * A generator may have a return type of Iterator, Iterable, or IterableIterator. + * This function can be used to extract the iterated type from this return type for + * contextual typing and verifying signatures. */ function getIteratedTypeOfIterableIterator(type: Type): Type { if (isTypeAny(type)) { @@ -17554,10 +17583,8 @@ namespace ts { * 1. Some constituent is neither a string nor an array. * 2. Some constituent is a string and target is less than ES5 (because in ES3 string is not indexable). */ - function getIteratedTypeOfPseudoIterableOrElementTypeOfArrayOrString(arrayOrStringType: Type, errorNode: Node, checkAssignability: boolean): Type { - Debug.assert(languageVersion < ScriptTarget.ES2015); - - const iteratedType = getIteratedTypeOfIterable(arrayOrStringType, /*errorNode*/ undefined); + function getIteratedTypeOfIterableOrElementTypeOfArrayOrString(arrayOrStringType: Type, errorNode: Node, checkAssignability: boolean, arrayLikeOnly: boolean): Type { + const iteratedType = !arrayLikeOnly && getIteratedTypeOfIterable(arrayOrStringType, /*errorNode*/ undefined); if (iteratedType) { if (checkAssignability && errorNode) { checkTypeAssignableTo(arrayOrStringType, createIterableType(iteratedType), errorNode); @@ -17578,6 +17605,7 @@ namespace ts { else if (arrayOrStringType.flags & TypeFlags.StringLike) { arrayType = neverType; } + const hasStringConstituent = arrayOrStringType !== arrayType; let reportedError = false; if (hasStringConstituent) { @@ -17603,8 +17631,12 @@ namespace ts { // But if the input was just number, we want to say that number is not an array type // or a string type. const diagnostic = hasStringConstituent - ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_an_iterator_method_that_returns_an_iterator - : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_an_iterator_method_that_returns_an_iterator; + ? arrayLikeOnly + ? Diagnostics.Type_0_is_not_an_array_type + : Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator + : arrayLikeOnly + ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type + : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator; error(errorNode, diagnostic, typeToString(arrayType)); } } @@ -17624,10 +17656,8 @@ namespace ts { return arrayElementType; } - function getIteratedTypeOfPseudoIterableOrElementTypeOfArray(inputType: Type, errorNode: Node, checkAssignability: boolean): Type { - Debug.assert(languageVersion < ScriptTarget.ES2015); - - const iteratedType = getIteratedTypeOfIterable(inputType, /*errorNode*/ undefined); + function getIteratedTypeOfIterableOrElementTypeOfArray(inputType: Type, errorNode: Node, checkAssignability: boolean, arrayLikeOnly: boolean): Type { + const iteratedType = !arrayLikeOnly && getIteratedTypeOfIterable(inputType, /*errorNode*/ undefined); if (iteratedType) { if (checkAssignability && errorNode) { checkTypeAssignableTo(inputType, createIterableType(iteratedType), errorNode); @@ -17638,7 +17668,10 @@ namespace ts { return getIndexTypeOfType(inputType, IndexKind.Number); } if (errorNode) { - error(errorNode, Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_an_iterator_method_that_returns_an_iterator, typeToString(inputType)); + const diagnostic = arrayLikeOnly + ? Diagnostics.Type_0_is_not_an_array_type + : Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator; + error(errorNode, diagnostic, typeToString(inputType)); } return undefined; } @@ -19803,7 +19836,7 @@ namespace ts { // for ( { a } of elems) { // } if (expr.parent.kind === SyntaxKind.ForOfStatement) { - const iteratedType = checkRightHandSideOfForOf((expr.parent).expression, (expr.parent).awaitKeyword !== undefined); + const iteratedType = checkRightHandSideOfForOf((expr.parent).expression, getForOfModifierKind(expr.parent)); return checkDestructuringAssignment(expr, iteratedType || unknownType); } // If this is from "for" initializer @@ -20532,33 +20565,8 @@ namespace ts { getGlobalPromiseConstructorSymbol = memoize(() => getGlobalValueSymbol("Promise")); tryGetGlobalPromiseConstructorSymbol = memoize(() => getGlobalSymbol("Promise", SymbolFlags.Value, /*diagnostic*/ undefined) && getGlobalPromiseConstructorSymbol()); getGlobalPromiseConstructorLikeType = memoize(() => getGlobalType("PromiseConstructorLike")); - getGlobalTemplateStringsArrayType = memoize(() => getGlobalType("TemplateStringsArray")); - if (languageVersion >= ScriptTarget.ES2017) { - getGlobalAsyncIteratorType = memoize(() => getGlobalType("AsyncIterator", /*arity*/ 1)); - getGlobalAsyncIterableType = memoize(() => getGlobalType("AsyncIterable", /*arity*/ 1)); - getGlobalAsyncIterableIteratorType = memoize(() => getGlobalType("AsyncIterableIterator", /*arity*/ 1)); - } - else { - getGlobalAsyncIteratorType = memoize(() => getGlobalType("PseudoAsyncIterator", /*arity*/ 1)); - getGlobalAsyncIterableType = memoize(() => getGlobalType("PseudoAsyncIterable", /*arity*/ 1)); - getGlobalAsyncIterableIteratorType = memoize(() => getGlobalType("PseudoAsyncIterableIterator", /*arity*/ 1)); - } - - if (languageVersion >= ScriptTarget.ES2015) { - getGlobalESSymbolType = memoize(() => getGlobalType("Symbol")); - getGlobalIterableType = memoize(() => getGlobalType("Iterable", /*arity*/ 1)); - getGlobalIterableIteratorType = memoize(() => getGlobalType("IterableIterator", /*arity*/ 1)); - } - else { - getGlobalESSymbolType = memoize(() => emptyObjectType); - getGlobalIterableType = memoize(() => getGlobalType("PseudoIterable", /*arity*/ 1)); - getGlobalIterableIteratorType = memoize(() => getGlobalType("PseudoIterableIterator", /*arity*/ 1)); - } - - getGlobalIteratorType = memoize(() => getGlobalType("Iterator", /*arity*/ 1)); - anyArrayType = createArrayType(anyType); autoArrayType = createArrayType(autoType); @@ -21277,9 +21285,9 @@ namespace ts { return true; } - if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && forInOrOfStatement.awaitKeyword) { + if (forInOrOfStatement.kind === SyntaxKind.ForOfStatement && getForOfModifierKind(forInOrOfStatement) === SyntaxKind.AwaitKeyword) { if ((forInOrOfStatement.flags & NodeFlags.AwaitContext) === NodeFlags.None) { - return grammarErrorOnNode(forInOrOfStatement.awaitKeyword, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator); + return grammarErrorOnNode(forInOrOfStatement.modifierToken, Diagnostics.A_for_await_of_statement_is_only_allowed_within_an_async_function_or_async_generator); } } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 239ec1ce22a..3a6be9c7f94 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1463,7 +1463,7 @@ "category": "Error", "code": 2460 }, - "Type '{0}' is not an array type or does not have an '__iterator__()' method that returns an iterator.": { + "Type '{0}' is not an array type.": { "category": "Error", "code": 2461 }, @@ -1587,7 +1587,7 @@ "category": "Error", "code": 2494 }, - "Type '{0}' is not an array type or a string type or does not have an '__iterator__()' method that returns an iterator.": { + "Type '{0}' is not an array type or a string type.": { "category": "Error", "code": 2495 }, @@ -1783,6 +1783,14 @@ "category": "Error", "code": 2543 }, + "Type '{0}' is not an array type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2544 + }, + "Type '{0}' is not an array type or a string type or does not have a '[Symbol.iterator]()' method that returns an iterator.": { + "category": "Error", + "code": 2545 + }, "JSX element attributes type '{0}' may not be a union type.": { "category": "Error", "code": 2600 diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 21c8271a2c0..ce6d6a4d933 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1368,7 +1368,7 @@ namespace ts { function emitForOfStatement(node: ForOfStatement) { const openParenPos = writeToken(SyntaxKind.ForKeyword, node.pos); write(" "); - emitWithSuffix(node.awaitKeyword, " "); + emitWithSuffix(node.modifierToken, " "); writeToken(SyntaxKind.OpenParenToken, openParenPos); emitForBinding(node.initializer); write(" of "); diff --git a/src/compiler/factory.ts b/src/compiler/factory.ts index e87758e27af..dc522f8e0a0 100644 --- a/src/compiler/factory.ts +++ b/src/compiler/factory.ts @@ -926,17 +926,18 @@ namespace ts { return node; } - export function createForOf(initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { + export function createForOf(modifierToken: AwaitKeywordToken | EachKeywordToken, initializer: ForInitializer, expression: Expression, statement: Statement, location?: TextRange) { const node = createNode(SyntaxKind.ForOfStatement, location); + node.modifierToken = modifierToken; node.initializer = initializer; node.expression = expression; node.statement = statement; return node; } - export function updateForOf(node: ForOfStatement, initializer: ForInitializer, expression: Expression, statement: Statement) { - if (node.initializer !== initializer || node.expression !== expression || node.statement !== statement) { - return updateNode(createForOf(initializer, expression, statement, node), node); + export function updateForOf(node: ForOfStatement, modifierToken: AwaitKeywordToken | EachKeywordToken, initializer: ForInitializer, expression: Expression, statement: Statement) { + if (node.modifierToken !== modifierToken || node.initializer !== initializer || node.expression !== expression || node.statement !== statement) { + return updateNode(createForOf(modifierToken, initializer, expression, statement, node), node); } return node; } @@ -1768,7 +1769,7 @@ namespace ts { scoped: false, text: ` var __values = (this && this.__values) || function (o) { - var i = o.__iterator__ || 0, d; + var i = typeof Symbol === "function" && o[Symbol.iterator] || 0, d; return i ? i.call(o) : { next: function () { return { done: d = d || i >= o.length, value: d ? void 0 : o[i++] }; } }; };` }; @@ -1827,7 +1828,7 @@ namespace ts { scoped: false, text: ` var __read = (this && this.__read) || function (o, n) { - if (!(m = o.__iterator__)) return o; + if (!(m = typeof Symbol === "function" && o[Symbol.iterator])) return o; var m, i = m.call(o), ar = [], r, e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } @@ -1906,8 +1907,14 @@ namespace ts { } } - - // Utilities + export function insertLeadingStatement(dest: Statement, source: Statement) { + if (isBlock(dest)) { + return updateBlock(dest, createNodeArray([source, ...dest.statements], dest.statements)); + } + else { + return createBlock(createNodeArray([dest, source]), /*location*/ undefined, /*multiLine*/ true); + } + } export interface CallBinding { target: LeftHandSideExpression; diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 145dc2f6b04..2a55adf20af 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -240,7 +240,7 @@ namespace ts { visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); case SyntaxKind.ForOfStatement: - return visitNode(cbNode, (node).awaitKeyword) || + return visitNode(cbNode, (node).modifierToken) || visitNode(cbNode, (node).initializer) || visitNode(cbNode, (node).expression) || visitNode(cbNode, (node).statement); @@ -4427,7 +4427,7 @@ namespace ts { function parseForOrForInOrForOfStatement(): Statement { const pos = getNodePos(); parseExpected(SyntaxKind.ForKeyword); - const awaitKeyword = parseOptionalToken(SyntaxKind.AwaitKeyword); + const modifierToken = parseOptionalToken(SyntaxKind.AwaitKeyword) || parseOptionalToken(SyntaxKind.EachKeyword); parseExpected(SyntaxKind.OpenParenToken); let initializer: VariableDeclarationList | Expression = undefined; @@ -4440,9 +4440,9 @@ namespace ts { } } let forOrForInOrForOfStatement: IterationStatement; - if (awaitKeyword ? parseExpected(SyntaxKind.OfKeyword) : parseOptional(SyntaxKind.OfKeyword)) { + if (modifierToken ? parseExpected(SyntaxKind.OfKeyword) : parseOptional(SyntaxKind.OfKeyword)) { const forOfStatement = createNode(SyntaxKind.ForOfStatement, pos); - forOfStatement.awaitKeyword = awaitKeyword; + forOfStatement.modifierToken = modifierToken; forOfStatement.initializer = initializer; forOfStatement.expression = allowInAnd(parseAssignmentExpressionOrHigher); parseExpected(SyntaxKind.CloseParenToken); diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 82302e98e37..990b461fe5f 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -126,6 +126,7 @@ namespace ts { "yield": SyntaxKind.YieldKeyword, "async": SyntaxKind.AsyncKeyword, "await": SyntaxKind.AwaitKeyword, + "each": SyntaxKind.EachKeyword, "of": SyntaxKind.OfKeyword, "{": SyntaxKind.OpenBraceToken, "}": SyntaxKind.CloseBraceToken, diff --git a/src/compiler/transformers/es2017.ts b/src/compiler/transformers/es2017.ts index f6bd945f28e..281056251a1 100644 --- a/src/compiler/transformers/es2017.ts +++ b/src/compiler/transformers/es2017.ts @@ -154,7 +154,7 @@ namespace ts { function visitLabeledStatement(node: LabeledStatement): VisitResult { const enclosedStatement = getEnclosedStatement(node); if (enclosedStatement.statement.kind === SyntaxKind.ForOfStatement && - (enclosedStatement.statement).awaitKeyword) { + (enclosedStatement.statement).modifierToken) { return visitForOfStatement(node.statement, enclosedStatement.enclosingLabeledStatements); } @@ -162,7 +162,7 @@ namespace ts { } function visitForOfStatement(node: ForOfStatement, enclosingLabeledStatements: LabeledStatement[]): VisitResult { - if (!node.awaitKeyword) return visitEachChild(node, visitor, context); + if (!node.modifierToken) return visitEachChild(node, visitor, context); let bodyLocation: TextRange; let statementsLocation: TextRange; @@ -774,8 +774,8 @@ namespace ts { scoped: false, text: ` var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { - var g = generator.apply(thisArg, _arguments || []), q = [], c; - return { next: verb("next"), "throw": verb("throw"), "return": verb("return"), __asyncIterator__: function () { return this; } }; + var g = generator.apply(thisArg, _arguments || []), q = [], c, i; + return i = { next: verb("next"), "throw": verb("throw"), "return": verb("return") }, i[Symbol.asyncIterator] = function () { return this; }, i; function verb(n) { return function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]), next(); }); }; } function next() { if (!c && q.length) resume((c = q.shift())[0], c[1]); } function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(c[3], e); } } @@ -808,9 +808,9 @@ namespace ts { name: "typescript:asyncValues", scoped: false, text: ` - var __asyncValues = (this && this.__asyncIterator) || function (o, iterator) { - var m; - return (m = o.__asyncIterator__) ? m.call(o) : typeof __values === "function" ? __values(o) : o[iterator || Symbol.iterator](); + var __asyncValues = (this && this.__asyncIterator) || function (o) { + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); };` }; @@ -832,9 +832,9 @@ namespace ts { name: "typescript:asyncDelegator", scoped: false, text: ` - var __asyncDelegator = (this && this.__asyncDelegator) || function (o, iterator) { + var __asyncDelegator = (this && this.__asyncDelegator) || function (o) { var i = { next: verb("next"), "throw": verb("throw", function (e) { throw e; }), "return": verb("return", function (v) { return { value: v, done: true }; }) }; - return o = __asyncValues(o, iterator), i[iterator || Symbol.iterator] = function () { return this; }, i; + return o = __asyncValues(o), i[Symbol.iterator] = function () { return this; }, i; function verb(n, f) { return function (v) { return { value: ["delegate", (o[n] || f).call(o, v)], done: false }; }; } };` }; diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index ceb3dbee12f..13ca656d518 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -234,6 +234,7 @@ namespace ts { : createBlock(append(leadingStatements, statement), statement, /*multiLine*/ true); return updateForOf( node, + node.modifierToken, createVariableDeclarationList( [ createVariableDeclaration(temp, /*type*/ undefined, /*initializer*/ undefined, node.initializer) diff --git a/src/compiler/transformers/generators.ts b/src/compiler/transformers/generators.ts index 7ed78ac972c..7fc7e7e6986 100644 --- a/src/compiler/transformers/generators.ts +++ b/src/compiler/transformers/generators.ts @@ -3169,8 +3169,8 @@ namespace ts { priority: 6, text: ` var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t; - return { next: verb(0), "throw": verb(1), "return": verb(2), __iterator__: function () { return this; } }; + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); diff --git a/src/compiler/transformers/module/system.ts b/src/compiler/transformers/module/system.ts index a4b17c12064..dc0996af79c 100644 --- a/src/compiler/transformers/module/system.ts +++ b/src/compiler/transformers/module/system.ts @@ -1261,6 +1261,7 @@ namespace ts { node = updateForOf( node, + node.modifierToken, visitForInitializer(node.initializer), visitNode(node.expression, destructuringVisitor, isExpression), visitNode(node.statement, nestedElementVisitor, isStatement, /*optional*/ false, liftToBlock) diff --git a/src/compiler/transformers/ts.ts b/src/compiler/transformers/ts.ts index 1427ca357d7..9c1b57f0885 100644 --- a/src/compiler/transformers/ts.ts +++ b/src/compiler/transformers/ts.ts @@ -464,6 +464,9 @@ namespace ts { // TypeScript namespace or external module import. return visitImportEqualsDeclaration(node); + case SyntaxKind.ForOfStatement: + return visitForOfStatement(node); + default: Debug.failBadSyntaxKind(node); return visitEachChild(node, visitor, context); @@ -2218,6 +2221,33 @@ namespace ts { return parameter; } + function visitForOfStatement(node: ForOfStatement): Statement { + if (getForOfModifierKind(node) !== SyntaxKind.EachKeyword) { + return visitEachChild(node, visitor, context); + } + + const counter = createLoopVariable(); + const rhsReference = node.expression.kind === SyntaxKind.Identifier + ? createUniqueName((node.expression).text) + : createTempVariable(/*recordTempVariable*/ undefined); + const binding = createForOfBindingStatement(node.initializer, createElementAccess(rhsReference, counter)); + const newNode = createFor( + createVariableDeclarationList([ + createVariableDeclaration(counter, /*type*/ undefined, createLiteral(0), /*location*/ moveRangePos(node.expression, -1)), + createVariableDeclaration(rhsReference, /*type*/ undefined, node.expression, /*location*/ node.expression) + ]), + createLessThan( + counter, + createPropertyAccess(rhsReference, "length"), + /*location*/ node.expression + ), + createPostfixIncrement(counter, /*location*/ node.expression), + insertLeadingStatement(node.statement, binding) + ); + + return visitNode(newNode, visitor, isStatement); + } + /** * Visits a variable statement in a namespace. * diff --git a/src/compiler/types.ts b/src/compiler/types.ts index a0d68eac314..8547b53cedf 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -166,6 +166,7 @@ namespace ts { BooleanKeyword, ConstructorKeyword, DeclareKeyword, + EachKeyword, GetKeyword, IsKeyword, KeyOfKeyword, @@ -519,6 +520,7 @@ namespace ts { export type AtToken = Token; export type ReadonlyToken = Token; export type AwaitKeywordToken = Token; + export type EachKeywordToken = Token; export type Modifier = Token @@ -1617,7 +1619,7 @@ namespace ts { export interface ForOfStatement extends IterationStatement { kind: SyntaxKind.ForOfStatement; - awaitKeyword?: AwaitKeywordToken; + modifierToken?: AwaitKeywordToken | EachKeywordToken; initializer: ForInitializer; expression: Expression; } diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f38c3b24aba..f3c85c9bd0f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -874,6 +874,11 @@ namespace ts { return false; } + export function getForOfModifierKind(node: ForOfStatement) { + return node.modifierToken + && node.modifierToken.kind; + } + export function isIterationStatement(node: Node, lookInLabeledStatements: boolean): node is IterationStatement { switch (node.kind) { case SyntaxKind.ForStatement: @@ -3668,10 +3673,6 @@ namespace ts { return node.symbol && getDeclarationOfKind(node.symbol, kind) === node; } - export function isEffectiveExternalModule(node: SourceFile, compilerOptions: CompilerOptions) { - return isExternalModule(node) || compilerOptions.isolatedModules; - } - // Node tests // // All node tests in the following list should *not* reference parent pointers so that diff --git a/src/compiler/visitor.ts b/src/compiler/visitor.ts index afa3bd10761..34dbd8fed0d 100644 --- a/src/compiler/visitor.ts +++ b/src/compiler/visitor.ts @@ -989,6 +989,7 @@ namespace ts { case SyntaxKind.ForOfStatement: return updateForOf(node, + (node).modifierToken, visitNode((node).initializer, visitor, isForInitializer), visitNode((node).expression, visitor, isExpression), visitNode((node).statement, visitor, isStatement, /*optional*/ false, liftToBlock)); diff --git a/src/lib/es2015.iterable.d.ts b/src/lib/es2015.iterable.d.ts index cfad25b53ec..1da0d9a638a 100644 --- a/src/lib/es2015.iterable.d.ts +++ b/src/lib/es2015.iterable.d.ts @@ -8,6 +8,17 @@ interface SymbolConstructor { readonly iterator: symbol; } +interface IteratorResult { + done: boolean; + value: T; +} + +interface Iterator { + next(value?: any): IteratorResult; + return?(value?: any): IteratorResult; + throw?(e?: any): IteratorResult; +} + interface Iterable { [Symbol.iterator](): Iterator; } diff --git a/src/lib/esnext.asynciterable.d.ts b/src/lib/es2017.asynciterable.d.ts similarity index 89% rename from src/lib/esnext.asynciterable.d.ts rename to src/lib/es2017.asynciterable.d.ts index 623f6850682..8379ba5ba6c 100644 --- a/src/lib/esnext.asynciterable.d.ts +++ b/src/lib/es2017.asynciterable.d.ts @@ -1,4 +1,5 @@ /// +/// interface SymbolConstructor { /** diff --git a/src/lib/es2017.d.ts b/src/lib/es2017.d.ts index c234a9edb24..65a0fb5660c 100644 --- a/src/lib/es2017.d.ts +++ b/src/lib/es2017.d.ts @@ -2,3 +2,4 @@ /// /// /// +/// \ No newline at end of file diff --git a/src/lib/es5.d.ts b/src/lib/es5.d.ts index 851d9300c51..e402a96fade 100644 --- a/src/lib/es5.d.ts +++ b/src/lib/es5.d.ts @@ -1338,39 +1338,6 @@ interface PromiseLike { onrejected: (reason: any) => TResult2 | PromiseLike): PromiseLike; } -interface IteratorResult { - done: boolean; - value: T; -} - -interface Iterator { - next(value?: any): IteratorResult; - return?(value?: any): IteratorResult; - throw?(e?: any): IteratorResult; -} - -interface PseudoIterable { - __iterator__(): Iterator; -} - -interface PseudoIterableIterator extends Iterator { - __iterator__(): PseudoIterableIterator; -} - -interface PseudoAsyncIterator { - next(value?: any): PromiseLike>; - return?(value?: any): PromiseLike>; - throw?(e?: any): PromiseLike>; -} - -interface PseudoAsyncIterable { - __asyncIterator__(): PseudoAsyncIterator; -} - -interface PseudoAsyncIterableIterator extends PseudoAsyncIterator { - __asyncIterator__(): PseudoAsyncIterableIterator; -} - interface ArrayLike { readonly length: number; readonly [n: number]: T; diff --git a/src/lib/esnext.d.ts b/src/lib/esnext.d.ts index 71fab82a866..8c4e516e8c2 100644 --- a/src/lib/esnext.d.ts +++ b/src/lib/esnext.d.ts @@ -1,2 +1,2 @@ /// -/// +///