Why `downlevelIteration` is not on by default?
Asked Answered
E

3

169

When targeting ES5 with usage of spread operator ... to convert an Iterator to an Array, it shows the error to use -downlevelIteration compiler option. Once it is on, spread operators seem just work flawlessly.

I wonder why is there a need to specify this? Are there any downsides/limitation when it is enabled besides adding more emitted generated code from tslib?

Another example: Dynamically create array of N (eg. 3):

[...Array(3).keys()]  // output: [0, 1, 2]

It displays an error in VS Code:
enter image description here

Error message from tsc:

Type 'IterableIterator' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators.

Edit and view the code and error in TypeScript Playground

Evansville answered 23/11, 2018 at 5:54 Comment(7)
Oh, thanks, so right solution is actually to just set target to es6 in tsconfig.jsonToilet
@Toilet you cannot use es6 target as it is going to break if there is requirement to support legacy js runtimes like Internet ExplorerEvansville
Yeah, though I believe most people googling here are not restricted to so old browsers, this error seems to appear by default if you do not have a tsconfig.json file at all.Toilet
I just hit this in stackblitz.com with a new TS project where target is 'esnext', so I'm confused about @Klesun's first comment about es6 being the solution, but I found another workaround for this specific code is Array.from(Array(3).keys()).Pyaemia
@Pyaemia Hi, it's 2021 now. Can I set target to es6 simply?Karakoram
@xiaomiezhang you can try, assuming you don't have to support super-old browsers, but as I said I don't think "target": "es6" will be sufficient for [...Array(3).keys()] -- I think you'll need EITHER compiler option '--downlevelIteration' OR Array.from(Array(3).keys()).Pyaemia
@Pyaemia Your solution sounds like the optimal way to go for me at least. I just needed a "range" function (like everybody else here trying to compute [...Array(n).keys()] ). Thanks!Reich
S
109

After reading the release notes and the article Downlevel Iteration for ES3/ES5 in TypeScript, I believe the answer to this question is that downlevelIteration is disabled because you need to decide (via configuration) how you want TypeScript to handle the compilation of compatibility code (to support older versions of Javascript).

As the more lengthy explanation in the article makes clear, you have to make a decision as to if you want TypeScript to inline necessary helper functions (simple, but can result in larger production bundle size) or if you wish to configure TypeScript to use tslib as a dependency and then make calls to its external methods.

I highly recommend reading Downlevel Iteration for ES3/ES5 in TypeScript for a deeper understanding… and possibly an alternate solution to your initial issue.

Stoppage answered 11/3, 2019 at 18:37 Comment(1)
It gets interesting when Babel is used for compilation of the TypeScript Code and the TypeScript compiler is used for type-checking only. This is the case i.e. for projects created with Create React App or Vue CLI. I think in this case Babel will handle the "downlevel iteration" anyways, so "downlevel iteration" should be activated in tsconfig.json ... however in CRA it is not enabled when generating the project ...Tolman
A
88

tl;dr: in your case, use Array.from(foo):

Array.from(Array(3).keys())

Downleveling is TypeScript’s term for transpiling to an older version of JavaScript. This flag is to enable support for a more accurate implementation of how modern JavaScript iterates through new concepts in older JavaScript runtimes.

ECMAScript 6 added several new iteration primitives: the for / of loop (for (el of arr)), Array spread ([a, ...b]), argument spread (fn(...args)), and Symbol.iterator. --downlevelIteration allows for these iteration primitives to be used more accurately in ES5 environments if a Symbol.iterator implementation is present.

Since in the question we faced the case of array spread, lets dig this one. Here is an array spread:

// Make a new array who elements are 1 followed by the elements of arr2
const arr = [1, ...arr2];

Based on the description, it sounds easy to downlevel to ES5:

// The same, right?
const arr = [1].concat(arr2);

However, this is observably different in certain rare cases. For example, if an array has a “hole” in it, the missing index will create an own property if spreaded, but will not if built using concat:

// Make an array where the '1' element is missing
let missing = [0, , 1];
let spreaded = [...missing];
let concated = [].concat(missing);
// true
"1" in spreaded;
// false
"1" in concated;

downlevelIteration will use Symbol.iterator (if present) to more accurately emulate ES 6 behavior.

more informations and example here

Now in your case, you don't want to change this behavior just to generate a basic range. Instead of [...Array(3).keys()], you can rely on Array.from(Array(3).keys())

Adlar answered 3/9, 2021 at 13:58 Comment(0)
T
34

All you have to do is change es5 to es6 in the tsconfig.json file.

enter image description here

Trump answered 29/10, 2022 at 5:50 Comment(1)
Be warned: This can have side effects. To me, the memory usage when compiling my app increased dramatically, always resulting in a "heap out of memory" error.Egghead

© 2022 - 2024 — McMap. All rights reserved.