@@ -17980,16 +17980,92 @@ namespace ts {
1798017980 * of a iterable (if defined globally) or element type of an array like for ES2015 or earlier.
1798117981 */
1798217982 function getIteratedTypeOrElementType(inputType: Type, errorNode: Node, allowStringInput: boolean, checkAssignability: boolean): Type {
17983- if (languageVersion >= ScriptTarget.ES2015) {
17984- const iteratedType = getIteratedTypeOfIterable(inputType, errorNode);
17985- if (checkAssignability && errorNode && iteratedType) {
17986- checkTypeAssignableTo(inputType, createIterableType(iteratedType), errorNode);
17983+ const uplevelIteration = languageVersion >= ScriptTarget.ES2015;
17984+ const downlevelIteration = !uplevelIteration && compilerOptions.downlevelIteration;
17985+
17986+ // Get the iterated type of an `Iterable<T>` or `IterableIterator<T>` only in ES2015
17987+ // or higher, or when downlevelIteration is supplied.
17988+ if (uplevelIteration || downlevelIteration) {
17989+ // We only report errors for an invalid iterable type in ES2015 or higher.
17990+ const iteratedType = getIteratedTypeOfIterable(inputType, uplevelIteration ? errorNode : undefined);
17991+ if (iteratedType || uplevelIteration) {
17992+ // Normally we would perform this check in `checkIteratedTypeOrElementType`,
17993+ // but we need to perform it here as `checkIteratedTypeOrElementType` won't
17994+ // have enough context to know whether the input type actually conforms
17995+ // to `Iterable<T>`.
17996+ if (checkAssignability && errorNode && iteratedType) {
17997+ checkTypeAssignableTo(inputType, createIterableType(iteratedType), errorNode);
17998+ }
17999+ return iteratedType;
18000+ }
18001+ }
18002+
18003+ let arrayType = inputType;
18004+ let reportedError = false;
18005+ let hasStringConstituent = false;
18006+
18007+ // If strings are permitted, remove any string-like constituents from the array type.
18008+ // This allows us to find other non-string element types from an array unioned with
18009+ // a string.
18010+ if (allowStringInput) {
18011+ if (arrayType.flags & TypeFlags.Union) {
18012+ // After we remove all types that are StringLike, we will know if there was a string constituent
18013+ // based on whether the result of filter is a new array.
18014+ const arrayTypes = (<UnionType>inputType).types;
18015+ const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike));
18016+ if (filteredTypes !== arrayTypes) {
18017+ arrayType = getUnionType(filteredTypes, /*subtypeReduction*/ true);
18018+ }
18019+ }
18020+ else if (arrayType.flags & TypeFlags.StringLike) {
18021+ arrayType = neverType;
18022+ }
18023+ hasStringConstituent = arrayType !== inputType;
18024+ if (hasStringConstituent) {
18025+ if (languageVersion < ScriptTarget.ES5) {
18026+ if (errorNode) {
18027+ error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
18028+ reportedError = true;
18029+ }
18030+ }
18031+
18032+ // Now that we've removed all the StringLike types, if no constituents remain, then the entire
18033+ // arrayOrStringType was a string.
18034+ if (arrayType.flags & TypeFlags.Never) {
18035+ return stringType;
18036+ }
18037+ }
18038+ }
18039+
18040+ if (!isArrayLikeType(arrayType)) {
18041+ if (errorNode && !reportedError) {
18042+ // Which error we report depends on whether we allow strings or if there was a
18043+ // string constituent. For example, if the input type is number | string, we
18044+ // want to say that number is not an array type. But if the input was just
18045+ // number and string input is allowed, we want to say that number is not an
18046+ // array type or a string type.
18047+ const diagnostic = !allowStringInput || hasStringConstituent
18048+ ? downlevelIteration
18049+ ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
18050+ : Diagnostics.Type_0_is_not_an_array_type
18051+ : downlevelIteration
18052+ ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
18053+ : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
18054+ error(errorNode, diagnostic, typeToString(arrayType));
1798718055 }
17988- return iteratedType;
18056+ return hasStringConstituent ? stringType : undefined;
18057+ }
18058+
18059+ const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number);
18060+ if (hasStringConstituent && arrayElementType) {
18061+ // This is just an optimization for the case where arrayOrStringType is string | string[]
18062+ if (arrayElementType.flags & TypeFlags.StringLike) {
18063+ return stringType;
18064+ }
18065+
18066+ return getUnionType([arrayElementType, stringType], /*subtypeReduction*/ true);
1798918067 }
17990- return allowStringInput
17991- ? getIteratedTypeOfIterableOrElementTypeOfArrayOrString(inputType, errorNode, checkAssignability)
17992- : getIteratedTypeOfIterableOrElementTypeOfArray(inputType, errorNode, checkAssignability);
18068+ return arrayElementType;
1799318069 }
1799418070
1799518071 function checkIteratedTypeOfIterableOrAsyncIterable(asyncIterable: Type, errorNode: Node): Type {
@@ -18279,116 +18355,6 @@ namespace ts {
1827918355 getIteratedTypeOfIterator(type, /*errorNode*/ undefined);
1828018356 }
1828118357
18282- /**
18283- * This function does the following steps:
18284- * 1. Break up arrayOrStringType (possibly a union) into its string constituents and array constituents.
18285- * 2. Take the element types of the array constituents.
18286- * 3. Return the union of the element types, and string if there was a string constituent.
18287- *
18288- * For example:
18289- * string -> string
18290- * number[] -> number
18291- * string[] | number[] -> string | number
18292- * string | number[] -> string | number
18293- * string | string[] | number[] -> string | number
18294- *
18295- * It also errors if:
18296- * 1. Some constituent is neither a string nor an array.
18297- * 2. Some constituent is a string and target is less than ES5 (because in ES3 string is not indexable).
18298- */
18299- function getIteratedTypeOfIterableOrElementTypeOfArrayOrString(arrayOrStringType: Type, errorNode: Node, checkAssignability: boolean): Type {
18300- const iteratedType = compilerOptions.downlevelIteration && getIteratedTypeOfIterable(arrayOrStringType, /*errorNode*/ undefined);
18301- if (iteratedType) {
18302- if (checkAssignability && errorNode) {
18303- checkTypeAssignableTo(arrayOrStringType, createIterableType(iteratedType), errorNode);
18304- }
18305- return iteratedType;
18306- }
18307-
18308- let arrayType = arrayOrStringType;
18309- if (arrayOrStringType.flags & TypeFlags.Union) {
18310- // After we remove all types that are StringLike, we will know if there was a string constituent
18311- // based on whether the result of filter is a new array.
18312- const arrayTypes = (arrayOrStringType as UnionType).types;
18313- const filteredTypes = filter(arrayTypes, t => !(t.flags & TypeFlags.StringLike));
18314- if (filteredTypes !== arrayTypes) {
18315- arrayType = getUnionType(filteredTypes, /*subtypeReduction*/ true);
18316- }
18317- }
18318- else if (arrayOrStringType.flags & TypeFlags.StringLike) {
18319- arrayType = neverType;
18320- }
18321-
18322- const hasStringConstituent = arrayOrStringType !== arrayType;
18323- let reportedError = false;
18324- if (hasStringConstituent) {
18325- if (languageVersion < ScriptTarget.ES5) {
18326- if (errorNode) {
18327- error(errorNode, Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
18328- reportedError = true;
18329- }
18330- }
18331-
18332- // Now that we've removed all the StringLike types, if no constituents remain, then the entire
18333- // arrayOrStringType was a string.
18334- if (arrayType.flags & TypeFlags.Never) {
18335- return stringType;
18336- }
18337- }
18338-
18339- if (!isArrayLikeType(arrayType)) {
18340- if (errorNode) {
18341- if (!reportedError) {
18342- // Which error we report depends on whether there was a string constituent. For example,
18343- // if the input type is number | string, we want to say that number is not an array type.
18344- // But if the input was just number, we want to say that number is not an array type
18345- // or a string type.
18346- const diagnostic = hasStringConstituent
18347- ? compilerOptions.downlevelIteration
18348- ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
18349- : Diagnostics.Type_0_is_not_an_array_type
18350- : compilerOptions.downlevelIteration
18351- ? Diagnostics.Type_0_is_not_an_array_type_or_a_string_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
18352- : Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
18353- error(errorNode, diagnostic, typeToString(arrayType));
18354- }
18355- }
18356- return hasStringConstituent ? stringType : undefined;
18357- }
18358-
18359- const arrayElementType = getIndexTypeOfType(arrayType, IndexKind.Number);
18360- if (arrayElementType && hasStringConstituent) {
18361- // This is just an optimization for the case where arrayOrStringType is string | string[]
18362- if (arrayElementType.flags & TypeFlags.StringLike) {
18363- return stringType;
18364- }
18365-
18366- return getUnionType([arrayElementType, stringType], /*subtypeReduction*/ true);
18367- }
18368-
18369- return arrayElementType;
18370- }
18371-
18372- function getIteratedTypeOfIterableOrElementTypeOfArray(inputType: Type, errorNode: Node, checkAssignability: boolean): Type {
18373- const iteratedType = compilerOptions.downlevelIteration && getIteratedTypeOfIterable(inputType, /*errorNode*/ undefined);
18374- if (iteratedType) {
18375- if (checkAssignability && errorNode) {
18376- checkTypeAssignableTo(inputType, createIterableType(iteratedType), errorNode);
18377- }
18378- return iteratedType;
18379- }
18380- if (isArrayLikeType(inputType)) {
18381- return getIndexTypeOfType(inputType, IndexKind.Number);
18382- }
18383- if (errorNode) {
18384- const diagnostic = compilerOptions.downlevelIteration
18385- ? Diagnostics.Type_0_is_not_an_array_type_or_does_not_have_a_Symbol_iterator_method_that_returns_an_iterator
18386- : Diagnostics.Type_0_is_not_an_array_type;
18387- error(errorNode, diagnostic, typeToString(inputType));
18388- }
18389- return undefined;
18390- }
18391-
1839218358 function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
1839318359 // Grammar checking
1839418360 checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
0 commit comments