From 6babef417f3ac5ffde4beeccea915c19a0161f1f Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 11 Dec 2014 17:41:24 -0800 Subject: [PATCH] Rest element support in array literal destructuring assignment --- src/compiler/checker.ts | 28 +++++++++++---- .../diagnosticInformationMap.generated.ts | 1 + src/compiler/diagnosticMessages.json | 4 +++ src/compiler/emitter.ts | 34 +++++++++++++------ 4 files changed, 49 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 18922653e60..4b8437f4f1c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6689,15 +6689,25 @@ module ts { for (var i = 0; i < elements.length; i++) { var e = elements[i]; if (e.kind !== SyntaxKind.OmittedExpression) { - var propName = "" + i; - var type = sourceType.flags & TypeFlags.Any ? sourceType : - isTupleLikeType(sourceType) ? getTypeOfPropertyOfType(sourceType, propName) : - getIndexTypeOfType(sourceType, IndexKind.Number); - if (type) { - checkDestructuringAssignment(e, type, contextualMapper); + if (e.kind !== SyntaxKind.SpreadElementExpression) { + var propName = "" + i; + var type = sourceType.flags & TypeFlags.Any ? sourceType : + isTupleLikeType(sourceType) ? getTypeOfPropertyOfType(sourceType, propName) : + getIndexTypeOfType(sourceType, IndexKind.Number); + if (type) { + checkDestructuringAssignment(e, type, contextualMapper); + } + else { + error(e, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName); + } } else { - error(e, Diagnostics.Type_0_has_no_property_1, typeToString(sourceType), propName); + if (i === elements.length - 1) { + checkReferenceAssignment((e).expression, sourceType, contextualMapper); + } + else { + error(e, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern); + } } } } @@ -6715,6 +6725,10 @@ module ts { if (target.kind === SyntaxKind.ArrayLiteralExpression) { return checkArrayLiteralAssignment(target, sourceType, contextualMapper); } + return checkReferenceAssignment(target, sourceType, contextualMapper); + } + + function checkReferenceAssignment(target: Expression, sourceType: Type, contextualMapper?: TypeMapper): Type { var targetType = checkExpression(target, contextualMapper); if (checkReferenceExpression(target, Diagnostics.Invalid_left_hand_side_of_assignment_expression, Diagnostics.Left_hand_side_of_assignment_expression_cannot_be_a_constant)) { checkTypeAssignableTo(sourceType, targetType, target, /*headMessage*/ undefined); diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 17dbae83bf2..04049238661 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -294,6 +294,7 @@ module ts { Type_0_has_no_property_1_and_no_string_index_signature: { code: 2459, category: DiagnosticCategory.Error, key: "Type '{0}' has no property '{1}' and no string index signature." }, Type_0_has_no_property_1: { code: 2460, category: DiagnosticCategory.Error, key: "Type '{0}' has no property '{1}'." }, Type_0_is_not_an_array_type: { code: 2461, category: DiagnosticCategory.Error, key: "Type '{0}' is not an array type." }, + A_rest_element_must_be_last_in_an_array_destructuring_pattern: { code: 2462, category: DiagnosticCategory.Error, key: "A rest element must be last in an array destructuring pattern" }, Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." }, Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." }, Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." }, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a06b71d460e..6663ff057b8 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1173,6 +1173,10 @@ "category": "Error", "code": 2461 }, + "A rest element must be last in an array destructuring pattern": { + "category": "Error", + "code": 2462 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 18a5571906b..239a89c6694 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2947,7 +2947,16 @@ module ts { for (var i = 0; i < elements.length; i++) { var e = elements[i]; if (e.kind !== SyntaxKind.OmittedExpression) { - emitDestructuringAssignment(e, createElementAccess(value, createNumericLiteral(i))); + if (e.kind !== SyntaxKind.SpreadElementExpression) { + emitDestructuringAssignment(e, createElementAccess(value, createNumericLiteral(i))); + } + else { + if (i === elements.length - 1) { + value = ensureIdentifier(value); + emitAssignment((e).expression, value); + write(".slice(" + i + ")"); + } + } } } } @@ -3772,17 +3781,18 @@ module ts { increaseIndent(); emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); + emitTempDeclarations(/*newLine*/ true); var exportName = resolver.getExportAssignmentName(node); if (exportName) { writeLine(); - var exportAssignement = getFirstExportAssignment(node); - emitStart(exportAssignement); + var exportAssignment = getFirstExportAssignment(node); + emitStart(exportAssignment); write("return "); - emitStart(exportAssignement.exportName); + emitStart(exportAssignment.exportName); write(exportName); - emitEnd(exportAssignement.exportName); + emitEnd(exportAssignment.exportName); write(";"); - emitEnd(exportAssignement); + emitEnd(exportAssignment); } decreaseIndent(); writeLine(); @@ -3792,17 +3802,18 @@ module ts { function emitCommonJSModule(node: SourceFile, startIndex: number) { emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); + emitTempDeclarations(/*newLine*/ true); var exportName = resolver.getExportAssignmentName(node); if (exportName) { writeLine(); - var exportAssignement = getFirstExportAssignment(node); - emitStart(exportAssignement); + var exportAssignment = getFirstExportAssignment(node); + emitStart(exportAssignment); write("module.exports = "); - emitStart(exportAssignement.exportName); + emitStart(exportAssignment.exportName); write(exportName); - emitEnd(exportAssignement.exportName); + emitEnd(exportAssignment.exportName); write(";"); - emitEnd(exportAssignement); + emitEnd(exportAssignment); } } @@ -3858,6 +3869,7 @@ module ts { else { emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); + emitTempDeclarations(/*newLine*/ true); } emitLeadingComments(node.endOfFileToken);