You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Spread operator is not correctly translated into JS #8856
My target is ES5 so not sure if this is related, but instances of Set are incorrectly converted to slice when using the spread operator. According to the MDN docs (scroll down to the Relation with Array objects section) - I should be able to convert a Set instance to an array by simply doing:
let foo = [1,2,3];
let bar = new Set(foo);
[...bar] instanceof Array; // results in `true` in the Chrome console
But the TypeScript compiler converts [...bar] to bar.slice() which is not correct.
Reacted by Tanguy Krotoff, Kfir Erez, Mark Langer, Robert Koritnik, William Cantin, f and thebluetropics
The spread operator on Set, etc is only supported when targeting ES6, since it requires the type to implement [Symbol.iterator]() which only exists in ES6. When targeting ES5, the spread operator is only allowed for arrays (and maybe for strings and other array-likes in the future).
So the compiler emits a call to .slice() because it expects to be emitting it for arrays. Note that it also emits an error complaining about using the spread operator on a non-array, so there's not really a problem.
Arnav Singh (@Arnavion), if the compiler had spit out an error, I wouldn't have researched for a GH issue or a work-around as it would have been clear what the problem was. I didn't get an error until the code was actually executed in my application. Whether or not this is due to some TSC setting, I don't know, I just know it's an issue in our environment.
Of course that depends on how Adam Gordon (@icfantv) acquired the types for Set in the first place... Some polyfills would have easily have tricked TypeScript into thinking it could do what it couldn't do.
Kitson Kelly (@kitsonk) excellent point. I am upgrading an Angular 1 app to Angular 2 and have to manage the typings myself to some extent and am at the mercy of those typings. While I would like to believe that they are solid, I have run into issues as noted above where the compiler will not complain but the generated code is not valid.
The compiler has no way to know if a definition is correct or not. if the definition says a Set is an Array, then it will be treated like one.
If you are using typescript@next consider using the --lib flag to specify the ES6-compliant set definition in --lib es2015.collection. see #6974 for more information.
Mohamed Hegazy (@mhegazy), can you please elaborate on what you mean when you say the compiler has no way of knowing if a definition is correct?
If I say [...Set], the compiler should not be generating invalid JavaScript. It's almost like you're saying the compiler uses the typings files to not only determine what is right syntax, but the actual generated code as well. Surely that can't be right.
It is correct. When figuring out how to down emit something, the compiler has to understand what it can and cannot do. When looking at the spread operator and a target of ES5, it says to itself "I can spread things that look like Arrays". The only way it knows if something is an Array or not is using the types it can reason out from the code. If it is passed something that allegedly is an Array, it will do what its down emit code says, which is to do a .slice(). If it doesn't match one of the patterns it doesn't know how to down-emit, it throws.
I am not certain there is anything actionable here. We previously decided that TypeScript's default ES5 transpile for spread and for..of (which forgo precise runtime semantics over runtime performance) would remain as-is, and introduced --downlevelIteration as a way to opt-in to the slower but more correct runtime semantics.
Activity
icfantv commentedon May 28, 2016
My target is ES5 so not sure if this is related, but instances of
Setare incorrectly converted toslicewhen using the spread operator. According to the MDN docs (scroll down to the Relation with Array objects section) - I should be able to convert aSetinstance to an array by simply doing:But the TypeScript compiler converts
[...bar]tobar.slice()which is not correct.basarat commentedon May 29, 2016
Perhaps the emit should use
concatinstead ofslice:-/ :rose:icfantv commentedon May 29, 2016
I've found that
Array.from(Set)works, but you need to make sure you have the typings installed.Arnavion commentedon Jun 1, 2016
Regarding
your second question:Adam Gordon (@icfantv)'s question:The spread operator on
Set, etc is only supported when targeting ES6, since it requires the type to implement[Symbol.iterator]()which only exists in ES6. When targeting ES5, the spread operator is only allowed for arrays (and maybe for strings and other array-likes in the future).So the compiler emits a call to
.slice()because it expects to be emitting it for arrays. Note that it also emits an error complaining about using the spread operator on a non-array, so there's not really a problem.icfantv commentedon Jun 1, 2016
Arnav Singh (@Arnavion), if the compiler had spit out an error, I wouldn't have researched for a GH issue or a work-around as it would have been clear what the problem was. I didn't get an error until the code was actually executed in my application. Whether or not this is due to some TSC setting, I don't know, I just know it's an issue in our environment.
Arnavion commentedon Jun 1, 2016
Playground
kitsonk commentedon Jun 1, 2016
Of course that depends on how Adam Gordon (@icfantv) acquired the types for
Setin the first place... Some polyfills would have easily have tricked TypeScript into thinking it could do what it couldn't do.For example:
icfantv commentedon Jun 1, 2016
Kitson Kelly (@kitsonk) excellent point. I am upgrading an Angular 1 app to Angular 2 and have to manage the typings myself to some extent and am at the mercy of those typings. While I would like to believe that they are solid, I have run into issues as noted above where the compiler will not complain but the generated code is not valid.
mhegazy commentedon Jun 4, 2016
The compiler has no way to know if a definition is correct or not. if the definition says a
Setis anArray, then it will be treated like one.If you are using
typescript@nextconsider using the--libflag to specify the ES6-compliant set definition in--lib es2015.collection. see #6974 for more information.icfantv commentedon Jun 5, 2016
Mohamed Hegazy (@mhegazy), can you please elaborate on what you mean when you say the compiler has no way of knowing if a definition is correct?
If I say
[...Set], the compiler should not be generating invalid JavaScript. It's almost like you're saying the compiler uses the typings files to not only determine what is right syntax, but the actual generated code as well. Surely that can't be right.kitsonk commentedon Jun 5, 2016
It is correct. When figuring out how to down emit something, the compiler has to understand what it can and cannot do. When looking at the spread operator and a target of ES5, it says to itself "I can spread things that look like Arrays". The only way it knows if something is an Array or not is using the types it can reason out from the code. If it is passed something that allegedly is an Array, it will do what its down emit code says, which is to do a
.slice(). If it doesn't match one of the patterns it doesn't know how to down-emit, it throws.How else could it be done?
Arnavion commentedon Jun 5, 2016
Mohamed Hegazy (@mhegazy) I feel the issue got side-tracked. Can you reopen for the original issue?
44 remaining items
EKashpersky commentedon Jan 30, 2019
Is it going to be fixed?
I mean hey, it's almost 3 years old.
rbuckton commentedon Apr 29, 2019
I am not certain there is anything actionable here. We previously decided that TypeScript's default ES5 transpile for spread and for..of (which forgo precise runtime semantics over runtime performance) would remain as-is, and introduced
--downlevelIterationas a way to opt-in to the slower but more correct runtime semantics.Feature/support ie11 (#139)
Remove some redundant legacy code that turned sets to arrays.
Remove some redundant legacy code that turned sets to arrays.