Assigning properties using object spread
In the ConsolidatedGrid example from the Intersection types section, we assigned each property to our intersection individually. Depending on the effect that we are trying to achieve, there is another way that we could have created our <Grid & Margin> intersection type with less code. Using a spread operator, we could perform a shallow copy of the properties from one or more of our input types automatically.
First, let's see how we can rewrite our earlier example so that it automatically populates the margin information:
function ConsolidatedGrid(grid : Grid, margin : Margin) : Grid & Margin {
let consolidatedGrid = <Grid & Margin>{...margin};
consolidatedGrid.Width += grid.Width;
consolidatedGrid.Height += grid.Height;
consolidatedGrid.Padding = margin.Padding ? margin.Padding :
grid.Padding;
return consolidatedGrid;
}
When we are instantiating our consolidatedGrid function, this code copies in the properties from margin and fills them in. The triple dots (...) tell the compiler to treat this as a spread operation. As we have already populated Width and Height, we use += to simply add in the elements from the grid.
What happens if we wanted to apply both the values from grid and margin instead? To do this, we can change our instantiation to look like this:
let consolidatedGrid = <Grid & Margin>{…grid, ...margin};
This fills in the Grid values with the values from grid and then fills in the Margin values from margin. This tells us two things. The first is that the spread operation maps the appropriate property to the appropriate property. The second thing this tells us is that the order that it does this in is important. As margin and grid both have the same properties, the values set by grid are overwritten by the values set by margin. In order to set the properties so that we see the values from grid in Width and Height, we have to reverse the order of this line. In reality, of course, we can see the effect as follows:
let consolidatedGrid = <Grid & Margin>{...margin, …grid };
At this stage, we should really take a look at the JavaScript that TypeScript produces out of this. This is what the code looks like when we compile it using ES5:
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s,
p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
function ConsolidatedGrid(grid, margin) {
var consolidatedGrid = __assign({}, margin, grid);
consolidatedGrid.Width += grid.Width;
consolidatedGrid.Height += grid.Height;
consolidatedGrid.Padding = margin.Padding ? margin.Padding :
grid.Padding;
return consolidatedGrid;
}
If, however, we compile the code using the version ES2015 or later, the __assign function is removed and our ConsolidatedGrid JavaScript looks as follows:
function ConsolidatedGrid(grid, margin) {
let consolidatedGrid = Object.assign({}, margin, grid);
consolidatedGrid.Width += grid.Width;
consolidatedGrid.Height += grid.Height;
consolidatedGrid.Padding = margin.Padding ? margin.Padding :
grid.Padding;
return consolidatedGrid;
}
What we are seeing here is that TypeScript works hard to ensure that it can produce code that works regardless of which version of ECMAScript we are targeting. We didn't have to worry whether the feature was available or not; we left it to TypeScript to fill in the blanks for us.