typescriptcompiler-errors

Type must have a '[Symbol.iterator]()' method that returns an iterator, but the type is an array


I have the following code:

const rgxExpressionPropertyName = [/return \w+\.(\w+);?/, /\(?\w+\)?\s*=>\s*\w+\.(\w+);?/];

//for each rgx format
for (var rgx of rgxExpressionPropertyName)
{
   ...
}

and it's causing typescript compilation error Type 'RegExp[]' must have a '[Symbol.iterator]()' method that returns an iterator.

There are many questions about this error on SO, but they all involve trying to iterate something that Typescript doesn't know is iterable. But in this case, the variable is certainly iterable because it's a const array.

If I replace this with for (var rgx in rgxExpressionPropertyName), I do not get the error, but then I don't get to use the nicer for/of syntax.

It doesn't have anything to do with being an array of Regexp, because I get the same error with number[].

What's going on here?

The minimum configuration and code required are:

tsconfig.json:

{
  "include": [ "/js/MyUtils.ts"],
  "compileOnSave": true,
  "compilerOptions": 
  {
    "outFile":"Output.d.ts",
    "declaration": true,
    "emitDeclarationOnly": true,
    "newLine": "CRLF",
    "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    //i don't this does anything (though we do need to figure out modules) 
    //"module": "ES6", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": [ "dom", "es5" ],
    "allowJs": false, /* Allow javascript files to be compiled. */
    "strict": false, /* Enable all strict type-checking options. */
    "skipLibCheck": true, /* Skip type checking of declaration files. */
    "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
  }
}

MyUtils.ts:

const nums = [1, 2, 3]

for (var num of nums)
{
    console.log(num)
}


Solution

  • TL;DR: Make sure to set the lib compiler option to an appropriate environment you are targetting.


    What lib and target do

    This happens because your lib configuration excludes The usage of iterators - an ES6 / ES2015 feature. Note that setting "target": "ES2015" is not enough - the lib and target settings serve different purposes:

    Here is how that can differ,

    target in short

    The following code

    arr.map(x => x + 1);
    

    Using "target": "ES5" will compile to:

    arr.map(function(x) { return x + 1; });
    

    Playground Link

    As that is ES5 compliant code - arrow functions were introduced in ES6.

    With "target": "ES6" the compiled code would be

    arr.map(x => x + 1);
    

    Playground Link

    lib in short

    This is different from target as it does not cover syntax. You can always use an arrow function or class or other syntax features when writing TypeScript, since the syntax is valid. If the target prohibits some syntax feature (e.g., arrow functions), then transpilation will ensure that the TypeScript source code is converted to valid JavaScript.

    However, APIs are entirely separate thing. You cannot use use new Map() without lib option covering it, since that is not syntax, so it cannot be transpiled to work.

    The conflict when mixing target and lib

    Using for..of is a bit special because it can be transpiled but the transpiled code will vary with target and the API support of the transpiled code can be restricted by lib.

    When using

    {
      "compilerOptions": 
      {
        "target": "ES5",
        "lib": [ "es6" ],
        ...
      }
    }
    

    The code

    const nums = [1, 2, 3]
    
    for (var num of nums)
    {
    }
    

    transpiles to

    var nums = [1, 2, 3];
    for (var _i = 0, nums_1 = nums; _i < nums_1.length; _i++) {
        var num = nums_1[_i];
    }
    

    Playground Link

    Which is valid code given the target and the constraints. However, using the configuration:

    {
      "compilerOptions": 
      {
        "target": "ES6",
        "lib": [ "es5" ],
        ...
      }
    }
    

    Then the loop instead transpiles to:

    const nums = [1, 2, 3];
    for (var num of nums) {
    }
    

    Playground Link

    Which is now not valid given the constraint on APIs, since this code will not work in an ES5 environment.


    By default if you do not set the lib option, then the default would be determined by target. So, you should not get conflicting results from compilation.

    However, if you do specify lib then you need to make sure it matches the environment you are planning to run your code on.