Skip to content

Commit d96a0ad

Browse files
authored
Merge pull request #12248 from Microsoft/object-spread-rest-fixes
Object spread rest fixes
2 parents 57feab3 + 6c1f733 commit d96a0ad

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+484
-116
lines changed

src/compiler/checker.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11164,10 +11164,10 @@ namespace ts {
1116411164
return links.resolvedType;
1116511165
}
1116611166

11167-
function getObjectLiteralIndexInfo(node: ObjectLiteralExpression, properties: Symbol[], kind: IndexKind): IndexInfo {
11167+
function getObjectLiteralIndexInfo(propertyNodes: NodeArray<ObjectLiteralElementLike>, offset: number, properties: Symbol[], kind: IndexKind): IndexInfo {
1116811168
const propTypes: Type[] = [];
1116911169
for (let i = 0; i < properties.length; i++) {
11170-
if (kind === IndexKind.String || isNumericName(node.properties[i].name)) {
11170+
if (kind === IndexKind.String || isNumericName(propertyNodes[i + offset].name)) {
1117111171
propTypes.push(getTypeOfSymbol(properties[i]));
1117211172
}
1117311173
}
@@ -11193,7 +11193,9 @@ namespace ts {
1119311193
let hasComputedStringProperty = false;
1119411194
let hasComputedNumberProperty = false;
1119511195

11196-
for (const memberDecl of node.properties) {
11196+
let offset = 0;
11197+
for (let i = 0; i < node.properties.length; i++) {
11198+
const memberDecl = node.properties[i];
1119711199
let member = memberDecl.symbol;
1119811200
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
1119911201
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
@@ -11262,6 +11264,7 @@ namespace ts {
1126211264
return unknownType;
1126311265
}
1126411266
spread = getSpreadType(spread, type, /*isFromObjectLiteral*/ false);
11267+
offset = i + 1;
1126511268
continue;
1126611269
}
1126711270
else {
@@ -11315,8 +11318,8 @@ namespace ts {
1131511318
return createObjectLiteralType();
1131611319

1131711320
function createObjectLiteralType() {
11318-
const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.String) : undefined;
11319-
const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node, propertiesArray, IndexKind.Number) : undefined;
11321+
const stringIndexInfo = hasComputedStringProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.String) : undefined;
11322+
const numberIndexInfo = hasComputedNumberProperty ? getObjectLiteralIndexInfo(node.properties, offset, propertiesArray, IndexKind.Number) : undefined;
1132011323
const result = createAnonymousType(node.symbol, propertiesTable, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo);
1132111324
const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral;
1132211325
result.flags |= TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | (typeFlags & TypeFlags.PropagatingFlags);

src/compiler/emitter.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,21 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
3838
s = arguments[i];
3939
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
4040
t[p] = s[p];
41+
if (typeof Object.getOwnPropertySymbols === "function")
42+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++)
43+
t[p[i]] = s[p[i]];
4144
}
4245
return t;
4346
};`;
4447

4548
const restHelper = `
4649
var __rest = (this && this.__rest) || function (s, e) {
4750
var t = {};
48-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && !e.indexOf(p))
51+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4952
t[p] = s[p];
53+
if (typeof Object.getOwnPropertySymbols === "function")
54+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
55+
t[p[i]] = s[p[i]];
5056
return t;
5157
};`;
5258

src/compiler/transformers/destructuring.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -304,25 +304,31 @@ namespace ts {
304304
if (properties.length !== 1) {
305305
// For anything but a single element destructuring we need to generate a temporary
306306
// to ensure value is evaluated exactly once.
307-
// When doing so we want to hightlight the passed in source map node since thats the one needing this temp assignment
307+
// When doing so we want to highlight the passed in source map node since that's the one needing this temp assignment
308308
value = ensureIdentifier(value, /*reuseIdentifierExpressions*/ true, location, emitTempVariableAssignment);
309309
}
310310

311311
let bindingElements: ObjectLiteralElementLike[] = [];
312+
let computedTempVariables: Expression[];
312313
for (let i = 0; i < properties.length; i++) {
313314
const p = properties[i];
314315
if (p.kind === SyntaxKind.PropertyAssignment || p.kind === SyntaxKind.ShorthandPropertyAssignment) {
315316
if (!transformRest ||
316317
p.transformFlags & TransformFlags.ContainsSpreadExpression ||
317-
(p.kind === SyntaxKind.PropertyAssignment && p.initializer.transformFlags & TransformFlags.ContainsSpreadExpression)) {
318+
(p.kind === SyntaxKind.PropertyAssignment && p.initializer.transformFlags & TransformFlags.ContainsSpreadExpression) ||
319+
isComputedPropertyName(p.name)) {
318320
if (bindingElements.length) {
319321
emitRestAssignment(bindingElements, value, location, target);
320322
bindingElements = [];
321323
}
322324
const propName = <Identifier | LiteralExpression>(<PropertyAssignment>p).name;
323325
const bindingTarget = p.kind === SyntaxKind.ShorthandPropertyAssignment ? <ShorthandPropertyAssignment>p : (<PropertyAssignment>p).initializer || propName;
324326
// Assignment for bindingTarget = value.propName should highlight whole property, hence use p as source map node
325-
emitDestructuringAssignment(bindingTarget, createDestructuringPropertyAccess(value, propName), p);
327+
const propAccess = createDestructuringPropertyAccess(value, propName);
328+
if (isComputedPropertyName(propName)) {
329+
computedTempVariables = append(computedTempVariables, (propAccess as ElementAccessExpression).argumentExpression);
330+
}
331+
emitDestructuringAssignment(bindingTarget, propAccess, p);
326332
}
327333
else {
328334
bindingElements.push(p);
@@ -336,7 +342,7 @@ namespace ts {
336342
bindingElements = [];
337343
}
338344
const propName = (p as SpreadAssignment).expression as Identifier;
339-
const restCall = createRestCall(value, target.properties, p => p.name, target);
345+
const restCall = createRestCall(value, target.properties, p => p.name, target, computedTempVariables);
340346
emitDestructuringAssignment(propName, restCall, p);
341347
}
342348
}
@@ -413,17 +419,28 @@ namespace ts {
413419

414420
/** Given value: o, propName: p, pattern: { a, b, ...p } from the original statement
415421
* `{ a, b, ...p } = o`, create `p = __rest(o, ["a", "b"]);`*/
416-
function createRestCall<T extends Node>(value: Expression, elements: T[], getPropertyName: (element: T) => PropertyName, location: TextRange): Expression {
417-
const propertyNames: LiteralExpression[] = [];
422+
function createRestCall<T extends Node>(value: Expression, elements: T[], getPropertyName: (element: T) => PropertyName, location: TextRange, computedTempVariables: Expression[]): Expression {
423+
const propertyNames: Expression[] = [];
418424
for (let i = 0; i < elements.length - 1; i++) {
419-
if (isOmittedExpression(elements[i])) {
425+
const element = elements[i];
426+
if (isOmittedExpression(element)) {
420427
continue;
421428
}
422-
const str = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
423-
str.pos = location.pos;
424-
str.end = location.end;
425-
str.text = getTextOfPropertyName(getPropertyName(elements[i]));
426-
propertyNames.push(str);
429+
if (isComputedPropertyName(getPropertyName(element))) {
430+
// get the temp name and put that in there instead, like `_tmp + ""`
431+
const temp = computedTempVariables.shift();
432+
propertyNames.push(createConditional(createBinary(createTypeOf(temp),
433+
SyntaxKind.EqualsEqualsEqualsToken,
434+
createLiteral("symbol")),
435+
createToken(SyntaxKind.QuestionToken),
436+
temp,
437+
createToken(SyntaxKind.ColonToken),
438+
createBinary(temp, SyntaxKind.PlusToken, createLiteral(""))));
439+
}
440+
else {
441+
const propName = getTextOfPropertyName(getPropertyName(element));
442+
propertyNames.push(createLiteral(propName, location));
443+
}
427444
}
428445
const args = createSynthesizedNodeArray([value, createArrayLiteral(propertyNames, location)]);
429446
return createCall(createIdentifier("__rest"), undefined, args);
@@ -522,6 +539,7 @@ namespace ts {
522539
const elements = name.elements;
523540
const numElements = elements.length;
524541
let bindingElements: BindingElement[] = [];
542+
let computedTempVariables: Expression[];
525543
for (let i = 0; i < numElements; i++) {
526544
const element = elements[i];
527545
if (isOmittedExpression(element)) {
@@ -533,12 +551,15 @@ namespace ts {
533551
bindingElements = [];
534552
}
535553
const restCall = createRestCall(value,
536-
name.elements,
554+
elements, // name.elements,
537555
element => (element as BindingElement).propertyName || <Identifier>(element as BindingElement).name,
538-
name);
556+
name,
557+
computedTempVariables);
539558
emitBindingElement(element, restCall);
540559
}
541-
else if (transformRest && !(element.transformFlags & TransformFlags.ContainsSpreadExpression)) {
560+
else if (transformRest &&
561+
!(element.transformFlags & TransformFlags.ContainsSpreadExpression) &&
562+
!isComputedPropertyName(element.propertyName || element.name)) {
542563
// do not emit until we have a complete bundle of ES2015 syntax
543564
bindingElements.push(element);
544565
}
@@ -549,7 +570,11 @@ namespace ts {
549570
}
550571
// Rewrite element to a declaration with an initializer that fetches property
551572
const propName = element.propertyName || <Identifier>element.name;
552-
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
573+
const propAccess = createDestructuringPropertyAccess(value, propName);
574+
if (isComputedPropertyName(propName)) {
575+
computedTempVariables = append(computedTempVariables, (propAccess as ElementAccessExpression).argumentExpression);
576+
}
577+
emitBindingElement(element, propAccess);
553578
}
554579
}
555580
if (bindingElements.length) {

src/compiler/utilities.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1262,6 +1262,7 @@ namespace ts {
12621262
case SyntaxKind.Decorator:
12631263
case SyntaxKind.JsxExpression:
12641264
case SyntaxKind.JsxSpreadAttribute:
1265+
case SyntaxKind.SpreadAssignment:
12651266
return true;
12661267
case SyntaxKind.ExpressionWithTypeArguments:
12671268
return (<ExpressionWithTypeArguments>parent).expression === node && isExpressionWithTypeArgumentsInClassExtendsClause(parent);

tests/baselines/reference/importHelpersInTsx.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
2929
s = arguments[i];
3030
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
3131
t[p] = s[p];
32+
if (typeof Object.getOwnPropertySymbols === "function")
33+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++)
34+
t[p[i]] = s[p[i]];
3235
}
3336
return t;
3437
};

tests/baselines/reference/objectRest.js

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//// [objectRest.ts]
2-
let o = { a: 1, b: 'no' }
2+
var o = { a: 1, b: 'no' }
33
var { ...clone } = o;
44
var { a, ...justB } = o;
55
var { a, b: renamed, ...empty } = o;
@@ -31,31 +31,39 @@ class Removable {
3131
}
3232
var removable = new Removable();
3333
var { removed, ...removableRest } = removable;
34+
35+
let computed = 'b';
36+
let computed2 = 'a';
37+
var { [computed]: stillNotGreat, [computed2]: soSo, ...o } = o;
38+
({ [computed]: stillNotGreat, [computed2]: soSo, ...o } = o);
3439

3540

3641
//// [objectRest.js]
3742
var __rest = (this && this.__rest) || function (s, e) {
3843
var t = {};
39-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && !e.indexOf(p))
44+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4045
t[p] = s[p];
46+
if (typeof Object.getOwnPropertySymbols === "function")
47+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
48+
t[p[i]] = s[p[i]];
4149
return t;
4250
};
43-
let o = { a: 1, b: 'no' };
51+
var o = { a: 1, b: 'no' };
4452
var clone = __rest(o, []);
4553
var { a } = o, justB = __rest(o, ["a"]);
4654
var { a, b: renamed } = o, empty = __rest(o, ["a", "b"]);
47-
var { ['b']: renamed } = o, justA = __rest(o, ["b"]);
55+
var _a = 'b', renamed = o[_a], justA = __rest(o, [typeof _a === "symbol" ? _a : _a + ""]);
4856
var { 'b': renamed } = o, justA = __rest(o, ["b"]);
4957
var { b: { '0': n, '1': oooo } } = o, justA = __rest(o, ["b"]);
5058
let o2 = { c: 'terrible idea?', d: 'yes' };
5159
var { d: renamed } = o2, d = __rest(o2, ["d"]);
5260
let nestedrest;
53-
var { x } = nestedrest, _a = nestedrest.n1, { y } = _a, _b = _a.n2, { z } = _b, nr = __rest(_b.n3, []), restrest = __rest(nestedrest, ["x", "n1"]);
61+
var { x } = nestedrest, _b = nestedrest.n1, { y } = _b, _c = _b.n2, { z } = _c, nr = __rest(_c.n3, []), restrest = __rest(nestedrest, ["x", "n1"]);
5462
let complex;
55-
var _c = complex.x, { ka } = _c, nested = __rest(_c, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]);
56-
(_d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex);
57-
var _e = { x: 1, y: 2 }, { x } = _e, fresh = __rest(_e, ["x"]);
58-
(_f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"]), _f);
63+
var _d = complex.x, { ka } = _d, nested = __rest(_d, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]);
64+
(_e = complex.x, { ka } = _e, nested = __rest(_e, ["ka"]), { y: other } = complex, rest = __rest(complex, ["x", "y"]), complex);
65+
var _f = { x: 1, y: 2 }, { x } = _f, fresh = __rest(_f, ["x"]);
66+
(_g = { x: 1, y: 2 }, { x } = _g, fresh = __rest(_g, ["x"]), _g);
5967
class Removable {
6068
set z(value) { }
6169
get both() { return 12; }
@@ -64,4 +72,8 @@ class Removable {
6472
}
6573
var removable = new Removable();
6674
var { removed } = removable, removableRest = __rest(removable, ["removed"]);
67-
var _d, _f;
75+
let computed = 'b';
76+
let computed2 = 'a';
77+
var _h = computed, stillNotGreat = o[_h], _j = computed2, soSo = o[_j], o = __rest(o, [typeof _h === "symbol" ? _h : _h + "", typeof _j === "symbol" ? _j : _j + ""]);
78+
(_k = computed, stillNotGreat = o[_k], _l = computed2, soSo = o[_l], o = __rest(o, [typeof _k === "symbol" ? _k : _k + "", typeof _l === "symbol" ? _l : _l + ""]), o);
79+
var _e, _g, _k, _l;

0 commit comments

Comments
 (0)