function checkElementTypeOfArrayOrString(arrayOrStringType, errorNode) {
            ts.Debug.assert(languageVersion < 2 /* ES6 */);
            // After we remove all types that are StringLike, we will know if there was a string constituent
            // based on whether the remaining type is the same as the initial type.
            var arrayType = removeTypesFromUnionType(arrayOrStringType, 258 /* StringLike */, /*isTypeOfKind*/ true, /*allowEmptyUnionResult*/ true);
            var hasStringConstituent = arrayOrStringType !== arrayType;
            var reportedError = false;
            if (hasStringConstituent) {
                if (languageVersion < 1 /* ES5 */) {
                    error(errorNode, ts.Diagnostics.Using_a_string_in_a_for_of_statement_is_only_supported_in_ECMAScript_5_and_higher);
                    reportedError = true;
                }
                // Now that we've removed all the StringLike types, if no constituents remain, then the entire
                // arrayOrStringType was a string.
                if (arrayType === emptyObjectType) {
                    return stringType;
                }
            }
            if (!isArrayLikeType(arrayType)) {
                if (!reportedError) {
                    // Which error we report depends on whether there was a string constituent. For example,
                    // if the input type is number | string, we want to say that number is not an array type.
                    // But if the input was just number, we want to say that number is not an array type
                    // or a string type.
                    var diagnostic = hasStringConstituent
                        ? ts.Diagnostics.Type_0_is_not_an_array_type
                        : ts.Diagnostics.Type_0_is_not_an_array_type_or_a_string_type;
                    error(errorNode, diagnostic, typeToString(arrayType));
                }
                return hasStringConstituent ? stringType : unknownType;
            }
            var arrayElementType = getIndexTypeOfType(arrayType, 1 /* Number */) || unknownType;
            if (hasStringConstituent) {
                // This is just an optimization for the case where arrayOrStringType is string | string[]
                if (arrayElementType.flags & 258 /* StringLike */) {
                    return stringType;
                }
                return getUnionType([arrayElementType, stringType]);
            }
            return arrayElementType;
        }
This function does the following steps:
  1. Break up arrayOrStringType (possibly a union) into its string constituents and array constituents.
  2. Take the element types of the array constituents.
  3. Return the union of the element types, and string if there was a string constitutent.

For example:
    string -> string
    number[] -> number
    string[] | number[] -> string | number
    string | number[] -> string | number
    string | string[] | number[] -> string | number

It also errors if:
  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).