Skip to content

Commit 6f274ea

Browse files
committed
Spread+rest fixes
1. Rename finds identifiers in spread assignment expressions. 2. Spreads with computed properties of type number or any no longer crash the compiler. 3. Rest emit uses indexOf === -1 instead of !indexOf to filter properties. 4. Rest emit correctly refers to computed properties' generated temps.
1 parent f437c8f commit 6f274ea

File tree

4 files changed

+53
-22
lines changed

4 files changed

+53
-22
lines changed

src/compiler/checker.ts

Lines changed: 9 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,10 @@ namespace ts {
1119311193
let hasComputedStringProperty = false;
1119411194
let hasComputedNumberProperty = false;
1119511195

11196-
for (const memberDecl of node.properties) {
11196+
let i = 0;
11197+
let offset = 0;
11198+
for (i = 0; i < node.properties.length; i++) {
11199+
const memberDecl = node.properties[i];
1119711200
let member = memberDecl.symbol;
1119811201
if (memberDecl.kind === SyntaxKind.PropertyAssignment ||
1119911202
memberDecl.kind === SyntaxKind.ShorthandPropertyAssignment ||
@@ -11262,6 +11265,7 @@ namespace ts {
1126211265
return unknownType;
1126311266
}
1126411267
spread = getSpreadType(spread, type, /*isFromObjectLiteral*/ false);
11268+
offset = i + 1;
1126511269
continue;
1126611270
}
1126711271
else {
@@ -11315,8 +11319,8 @@ namespace ts {
1131511319
return createObjectLiteralType();
1131611320

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

src/compiler/emitter.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ var __assign = (this && this.__assign) || Object.assign || function(t) {
4545
const restHelper = `
4646
var __rest = (this && this.__rest) || function (s, e) {
4747
var t = {};
48-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && !e.indexOf(p))
48+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) === -1)
4949
t[p] = s[p];
5050
return t;
5151
};`;

src/compiler/transformers/destructuring.ts

Lines changed: 42 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 = computedTempVariables || []).push((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 stringifiedTemp = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
432+
stringifiedTemp.pos = location.pos;
433+
stringifiedTemp.end = location.end;
434+
stringifiedTemp.text = "";
435+
propertyNames.push(createBinary(computedTempVariables.shift(), SyntaxKind.PlusToken, stringifiedTemp));
436+
}
437+
else {
438+
const str = <StringLiteral>createSynthesizedNode(SyntaxKind.StringLiteral);
439+
str.pos = location.pos;
440+
str.end = location.end;
441+
str.text = getTextOfPropertyName(getPropertyName(element));
442+
propertyNames.push(str);
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
}
@@ -548,8 +569,13 @@ namespace ts {
548569
bindingElements = [];
549570
}
550571
// Rewrite element to a declaration with an initializer that fetches property
572+
// TODO: Probably save the result of createDestructuringPropertyAccess if propName is a computed property
551573
const propName = element.propertyName || <Identifier>element.name;
552-
emitBindingElement(element, createDestructuringPropertyAccess(value, propName));
574+
const propAccess = createDestructuringPropertyAccess(value, propName);
575+
if (isComputedPropertyName(propName)) {
576+
(computedTempVariables = computedTempVariables || []).push((propAccess as ElementAccessExpression).argumentExpression);
577+
}
578+
emitBindingElement(element, propAccess);
553579
}
554580
}
555581
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);

0 commit comments

Comments
 (0)