Repository navigation
Support proposed ES Rest/Spread properties #2103
Description
es7 proposal : https://github.com/sebmarkbage/ecmascript-rest-spread
Spread properties
Typing
In my opinion the goal of this method is to be able to duplicate an object and changing some props, so I think it's particularly important in this case to not check duplicate property declaration :
var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3, y: 4}; // not an errorI have a very naive type check algorithm for a similar feature (JSXSpreadAttribute) in my little jsx-typescript fork: I just copy the properties of the spread object in the properties table when I encounter a spread object, and override those property if I encounter a declaration with a similar name.
Emitting
jstransform use Object.assign, babel introduce a shim:
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };We could either force the presence of assign function on ObjectConstructor interface, or provide a similar function (with a different name).
I think that the optimal solution would be to not emit any helper in es6 target (or if Object.assign is defined), and to emit an helper function for es5, es3.
var obj = { x: 1, y: 2};
var obj1 = {...obj, z: 3};
/// ES6 emit
var obj = {x: 1, y: 2};
var obj1= Object.assign({}, obj, { z: 3 });
//ES3 emit
var __assign = function (target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i];
for (var key in source) {
if (Object.prototype.hasOwnProperty.call(source, key)) {
target[key] = source[key];
}
}
}
return target;
};
var obj = {x: 1, y: 2};
var obj1= __assign({}, obj, { z: 3 });Rest properties
Typing
For simple object the new type is a subtype of the assignation that does not contains properties that has been captured before the rest properties :
var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;
obj1// {x: number; y:number};If the destructuring assignment has an index declaration, the result has also a similar index declaration:
var obj: { [string: string]: string };
var {[excludedId], ...obj1} = obj;
obj1// { [string: string]: string };new/call declarations are obviously not captured:
var obj: { (): void; property: string};
var { ...obj1} = obj;
obj1// { property: string };Emitting
It is not possible to emit rest properties without an helper function, this one is from babel:
var obj = {x:1, y: 1, z: 1};
var {z, ...obj1} = obj;var __objectWithoutProperties = function(obj, keys) {
var target = {};
for (var i in obj) {
if (keys.indexOf(i) >= 0) continue;
if (!Object.prototype.hasOwnProperty.call(obj, i)) continue;
target[i] = obj[i];
}
return target;
};
var obj = {x:1, y: 1, z: 1};
var z = obj.z;
var obj1 = __objectWithoutProperties(obj, ["z"]);Edit: added some little typing/emitting example
Activity
RyanCavanaugh commentedon Feb 23, 2015
We'd probably need some examples of how this could plausibly be emitted in downlevel (if that's in scope).
fdecampredon commentedon Feb 23, 2015
Ryan Cavanaugh (@RyanCavanaugh), I added some little emitting example and typing idea.
I would like to work on that one since anyway it's more or less a jsx feature and I will need to add it to my fork, but if it's added to typescript core it's better ^^.
mnpenner commentedon Jul 10, 2015
I recently discovered this feature in ES7 via this article. I have to say, it's quite handy!
Would love to see this in TypeScript!
prabirshrestha commentedon Jul 11, 2015
Any updates on this? Would love to see this in 1.6 as it would come super handy when using with React 😄
DanielRosenwasser commentedon Jul 11, 2015
Prabir Shrestha (@prabirshrestha) for the record, we currently have the property spread operator for JSX on
master.mnpenner commentedon Jul 11, 2015
Daniel Rosenwasser (@DanielRosenwasser) Could you elaborate? Does that mean this works on master with the --jsx flag? Does that include rest properties or just spread properties?
DanielRosenwasser commentedon Jul 11, 2015
Sorry Mark Penner (@mnpenner) and Prabir Shrestha (@prabirshrestha), yes, I meant on
master. Ryan Cavanaugh (@RyanCavanaugh) knows more about this than I do, but I believe it's just spread properties.RyanCavanaugh commentedon Jul 13, 2015
JSX supports spread attributes, e.g.
<TagName {...spreadedExpr} />. Nothing to do with the ES6 spread operator other than sharing the same token and having roughly equivalent semantics.RichiCoder1 commentedon Aug 7, 2015
I'm very interested in both Redux and React, the manipulation in state is made much easier with this. Would love to see this implemented.
Lenne231 commentedon Aug 17, 2015
Rest/Spread is approved for Stage 2 now https://github.com/tc39/tc39-notes/blob/master/es7/2015-07/july-30.md
90 remaining items
dsifford commentedon Nov 5, 2016
@SpyMaster356 I've been lurking this for a while and it looks like it's close. Nathan Shively-Sanders (@sandersn) has been kicking ass on this for (at least) the past few weeks. 🙌
You can follow along here (#12028) and here (#11150)
xooxdoo commentedon Nov 12, 2016
Some one should update the Roadmap
aaronbeall commentedon Dec 7, 2016
It seems that using this feature allows the assignment of unknown props:
I was expecting an error that
xis not a known property ofState. Is this not how the feature works?For example, my current workaround before this PR was this:
Is it possible to achieve this with object spread/rest?
mhegazy commentedon Dec 7, 2016
Object Rest and Spread, as per the ES proposal, behaves similar to
Object.assign. the last mention of the property "wins". no errors are reported. in the example you had, the type of{...state, x:"X"}is{ a: string, b:number, x:string }; and this type is assignable toStateand thus the assignment works. this is the same as sayinglet state: State = { a: "a", b:0, x: "X" };would be allowed as well.aaronbeall commentedon Dec 7, 2016
But that's what I'm confused about:
let state: State = { a: "a", b:0, x: "X" }gives the errorObject literal may only specify known properties, and 'x' does not exist in type 'State'which is what I want... why is it a valid assignment when coming out of an object literal containing a spread?mhegazy commentedon Dec 7, 2016
sorry.. object literals are a special case. my example was wrong. here is a better example:
The issue here is if rather subjective. When the compiler sees
let state:State = { a: "a", b:0, x: "X" }, chances arexis a typo, either a stale propoerty that was left off after refactoring, or a type for an optional property, that is why it is reported as an error.however, if you spread an object, let's say
let myConfig : State= { a: 1, ...myOtherBigConfigBag}, if themyOtherBigConfigBaghas a few properties that you do not care about, you just need theaand theb, an error here would force you to keep these two interfaces in sync, or cast to make these errors go away.that said. we should reconsider this decision. filed #12717 to track that.
aaronbeall commentedon Dec 8, 2016
I see. I love your idea in #12717, that's exactly what I was thinking. I would actually like such behavior even for the spread props but I can see your point that it's very subjective and might be annoying for some common JS use cases.
bondz commentedon Dec 8, 2016
Aaron Beall (@aaronbeall) I agree, it would be annoying for common JS use cases... Most of the time, you just want to ensure the object has the shape of the interface specified and not directly inspect the spread object, the current implementation is okay IMO
olmobrutall commentedon Dec 8, 2016
Hi guys, Congrats for the great release! Now is time for a deserved rest... speacking of whitch I've an issue with the rest operator :)
Using React you tipically have a component like:
The issue is that when you hover with the mouse over
attrsyou get the list of all the properties (hundreds) instead of React.HtmlAttributes.I know that typescript is structurally typed and all that, but could be possible to assign an explicit type to the rest variable?
Some alternatives:
aaronbeall commentedon Dec 8, 2016
Bond (@bondz) Well, its not true in 100% of the uses in my project. :) But in another project it may be very annoying. In my case I'm using Redux and React and making heavy use of immutable state, which means to update or create state objects I must copy all props onto a new object literal... in all cases I want 100% type safety that I'm trying to copy the right data onto the target state interface. Currently I use functions to ensure this safety, but I would prefer to use object spread because its cleaner (readable, expressive, standard syntax.) But in someone else's project they might want different behavior because they are using a lot of untyped or loose object structures, so I see how it's quite subjective. I think the #12717 suggestion is a good middle ground (and more consistent with existing object literal type safety.)
evisong commentedon Dec 16, 2016
Seems it's already state 3, any plan of supporting this?
Btw, we use VSCode mainly for ES development, not TS yet :)
bondz commentedon Dec 16, 2016
EviSONG (@evisong) this feature has shipped and is already available in the latest version of vsCode. Update your vsCode to version 1.8