@@ -17980,16 +17980,92 @@ namespace ts {
17980
17980
* of a iterable (if defined globally) or element type of an array like for ES2015 or earlier.
17981
17981
*/
17982
17982
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));
17987
18055
}
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);
17989
18067
}
17990
- return allowStringInput
17991
- ? getIteratedTypeOfIterableOrElementTypeOfArrayOrString(inputType, errorNode, checkAssignability)
17992
- : getIteratedTypeOfIterableOrElementTypeOfArray(inputType, errorNode, checkAssignability);
18068
+ return arrayElementType;
17993
18069
}
17994
18070
17995
18071
function checkIteratedTypeOfIterableOrAsyncIterable(asyncIterable: Type, errorNode: Node): Type {
@@ -18279,116 +18355,6 @@ namespace ts {
18279
18355
getIteratedTypeOfIterator(type, /*errorNode*/ undefined);
18280
18356
}
18281
18357
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
-
18392
18358
function checkBreakOrContinueStatement(node: BreakOrContinueStatement) {
18393
18359
// Grammar checking
18394
18360
checkGrammarStatementInAmbientContext(node) || checkGrammarBreakOrContinueStatement(node);
0 commit comments