diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c86906ca5b6..a7726b786b7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1674,19 +1674,26 @@ module ts { error(name, Diagnostics.Type_0_has_no_property_1_and_no_string_index_signature, typeToString(parentType), declarationNameToString(name)); return unknownType; } - return type; } - // For an array binding element the specified or inferred type of the parent must be assignable to any[] - if (!isTypeAssignableTo(parentType, anyArrayType)) { - error(pattern, Diagnostics.Type_0_is_not_an_array_type, typeToString(parentType)); - return unknownType; - } - // Use specific property type when parent is a tuple or numeric index type when parent is an array - var propName = "" + indexOf(pattern.elements, declaration); - var type = isTupleLikeType(parentType) ? getTypeOfPropertyOfType(parentType, propName) : getIndexTypeOfType(parentType, IndexKind.Number); - if (!type) { - error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName); - return unknownType; + else { + // For an array binding element the specified or inferred type of the parent must be assignable to any[] + if (!isTypeAssignableTo(parentType, anyArrayType)) { + error(pattern, Diagnostics.Type_0_is_not_an_array_type, typeToString(parentType)); + return unknownType; + } + if (!declaration.dotDotDotToken) { + // Use specific property type when parent is a tuple or numeric index type when parent is an array + var propName = "" + indexOf(pattern.elements, declaration); + var type = isTupleLikeType(parentType) ? getTypeOfPropertyOfType(parentType, propName) : getIndexTypeOfType(parentType, IndexKind.Number); + if (!type) { + error(declaration, Diagnostics.Type_0_has_no_property_1, typeToString(parentType), propName); + return unknownType; + } + } + else { + // Rest element has an array type with the same element type as the parent type + var type = createArrayType(getIndexTypeOfType(parentType, IndexKind.Number)); + } } return type; } @@ -1744,17 +1751,8 @@ module ts { return anyType; } - // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself - // and without regard to its context (i.e. without regard any type annotation or initializer associated with the - // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] - // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is - // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring - // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of - // the parameter. - function getTypeFromBindingPattern(pattern: BindingPattern): Type { - if (pattern.kind === SyntaxKind.ArrayBindingPattern) { - return createTupleType(map(pattern.elements, e => e.kind === SyntaxKind.OmittedExpression ? anyType : getTypeFromBindingElement(e))); - } + // Return the type implied by an object binding pattern + function getTypeFromObjectBindingPattern(pattern: BindingPattern): Type { var members: SymbolTable = {}; forEach(pattern.elements, e => { var flags = SymbolFlags.Property | SymbolFlags.Transient | (e.initializer ? SymbolFlags.Optional : 0); @@ -1766,6 +1764,32 @@ module ts { return createAnonymousType(undefined, members, emptyArray, emptyArray, undefined, undefined); } + // Return the type implied by an array binding pattern + function getTypeFromArrayBindingPattern(pattern: BindingPattern): Type { + var hasSpreadElement: boolean = false; + var elementTypes: Type[] = []; + forEach(pattern.elements, e => { + elementTypes.push(e.kind === SyntaxKind.OmittedExpression || e.dotDotDotToken ? anyType : getTypeFromBindingElement(e)); + if (e.dotDotDotToken) { + hasSpreadElement = true; + } + }); + return !elementTypes.length ? anyArrayType : hasSpreadElement ? createArrayType(getUnionType(elementTypes)) : createTupleType(elementTypes); + } + + // Return the type implied by a binding pattern. This is the type implied purely by the binding pattern itself + // and without regard to its context (i.e. without regard any type annotation or initializer associated with the + // declaration in which the binding pattern is contained). For example, the implied type of [x, y] is [any, any] + // and the implied type of { x, y: z = 1 } is { x: any; y: number; }. The type implied by a binding pattern is + // used as the contextual type of an initializer associated with the binding pattern. Also, for a destructuring + // parameter with no type annotation or initializer, the type implied by the binding pattern becomes the type of + // the parameter. + function getTypeFromBindingPattern(pattern: BindingPattern): Type { + return pattern.kind === SyntaxKind.ObjectBindingPattern + ? getTypeFromObjectBindingPattern(pattern) + : getTypeFromArrayBindingPattern(pattern); + } + // Return the type associated with a variable, parameter, or property declaration. In the simple case this is the type // specified in a type annotation or inferred from an initializer. However, in the case of a destructuring declaration it // is a bit more involved. For example: @@ -4510,6 +4534,7 @@ module ts { case SyntaxKind.VoidExpression: case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.ConditionalExpression: + case SyntaxKind.SpreadElementExpression: case SyntaxKind.Block: case SyntaxKind.VariableStatement: case SyntaxKind.ExpressionStatement: @@ -5306,15 +5331,37 @@ module ts { return false; } + function checkSpreadElementExpression(node: SpreadElementExpression, contextualMapper?: TypeMapper): Type { + var type = checkExpressionCached(node.expression, contextualMapper); + if (!isTypeAssignableTo(type, anyArrayType)) { + error(node.expression, Diagnostics.Type_0_is_not_an_array_type, typeToString(type)); + return unknownType; + } + return type; + } + function checkArrayLiteral(node: ArrayLiteralExpression, contextualMapper?: TypeMapper): Type { var elements = node.elements; if (!elements.length) { return createArrayType(undefinedType); } - var elementTypes = map(elements, e => checkExpression(e, contextualMapper)); - var contextualType = getContextualType(node); - if ((contextualType && contextualTypeIsTupleLikeType(contextualType)) || isAssignmentTarget(node)) { - return createTupleType(elementTypes); + var hasSpreadElement: boolean = false; + var elementTypes: Type[] = []; + forEach(elements, e => { + var type = checkExpression(e, contextualMapper); + if (e.kind === SyntaxKind.SpreadElementExpression) { + elementTypes.push(getIndexTypeOfType(type, IndexKind.Number) || anyType); + hasSpreadElement = true; + } + else { + elementTypes.push(type); + } + }); + if (!hasSpreadElement) { + var contextualType = getContextualType(node); + if (contextualType && contextualTypeIsTupleLikeType(contextualType) || isAssignmentTarget(node)) { + return createTupleType(elementTypes); + } } return createArrayType(getUnionType(elementTypes)); } @@ -6715,15 +6762,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); + } } } } @@ -6741,6 +6798,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); @@ -7081,6 +7142,8 @@ module ts { return checkBinaryExpression(node, contextualMapper); case SyntaxKind.ConditionalExpression: return checkConditionalExpression(node, contextualMapper); + case SyntaxKind.SpreadElementExpression: + return checkSpreadElementExpression(node, contextualMapper); case SyntaxKind.OmittedExpression: return undefinedType; case SyntaxKind.YieldExpression: @@ -7987,15 +8050,6 @@ module ts { // Check variable, parameter, or property declaration function checkVariableLikeDeclaration(node: VariableLikeDeclaration) { - // Grammar checking - // TODO (yuisu) : Revisit this check once move all grammar checking - if (node.kind === SyntaxKind.BindingElement) { - checkGrammarEvalOrArgumentsInStrictMode(node, node.name); - } - else if (node.kind === SyntaxKind.VariableDeclaration) { - checkGrammarVariableDeclaration(node); - } - checkSourceElement(node.type); // For a computed property, just check the initializer and exit if (hasComputedNameButNotSymbol(node)) { @@ -8051,6 +8105,16 @@ module ts { } } + function checkVariableDeclaration(node: VariableDeclaration) { + checkGrammarVariableDeclaration(node); + return checkVariableLikeDeclaration(node); + } + + function checkBindingElement(node: BindingElement) { + checkGrammarBindingElement(node); + return checkVariableLikeDeclaration(node); + } + function checkVariableStatement(node: VariableStatement) { // Grammar checking checkGrammarModifiers(node) || checkGrammarVariableDeclarations(node, node.declarations) || checkGrammarForDisallowedLetOrConstStatement(node); @@ -8094,7 +8158,7 @@ module ts { // Grammar checking checkGrammarForStatementInAmbientContext(node) || checkGrammarVariableDeclarations(node, node.declarations); - if (node.declarations) forEach(node.declarations, checkVariableLikeDeclaration); + if (node.declarations) forEach(node.declarations, checkVariableDeclaration); if (node.initializer) checkExpression(node.initializer); if (node.condition) checkExpression(node.condition); if (node.iterator) checkExpression(node.iterator); @@ -8120,7 +8184,7 @@ module ts { if (node.declarations) { if (node.declarations.length >= 1) { var decl = node.declarations[0]; - checkVariableLikeDeclaration(decl); + checkVariableDeclaration(decl); if (decl.type) { error(decl, Diagnostics.The_left_hand_side_of_a_for_in_statement_cannot_use_a_type_annotation); } @@ -9092,8 +9156,9 @@ module ts { case SyntaxKind.TryStatement: return checkTryStatement(node); case SyntaxKind.VariableDeclaration: + return checkVariableDeclaration(node); case SyntaxKind.BindingElement: - return checkVariableLikeDeclaration(node); + return checkBindingElement(node); case SyntaxKind.ClassDeclaration: return checkClassDeclaration(node); case SyntaxKind.InterfaceDeclaration: @@ -9172,6 +9237,7 @@ module ts { case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: + case SyntaxKind.SpreadElementExpression: case SyntaxKind.Block: case SyntaxKind.ModuleBlock: case SyntaxKind.VariableStatement: @@ -10535,6 +10601,22 @@ module ts { } } + function checkGrammarBindingElement(node: BindingElement) { + if (node.dotDotDotToken) { + var elements = (node.parent).elements; + if (node !== elements[elements.length - 1]) { + return grammarErrorOnNode(node, Diagnostics.A_rest_element_must_be_last_in_an_array_destructuring_pattern); + } + if (node.initializer) { + // Error on equals token which immediate precedes the initializer + return grammarErrorAtPos(getSourceFileOfNode(node), node.initializer.pos - 1, 1, Diagnostics.A_rest_element_cannot_have_an_initializer); + } + } + // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code + // and its Identifier is eval or arguments + return checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + } + function checkGrammarVariableDeclaration(node: VariableDeclaration) { if (isInAmbientContext(node)) { if (isBindingPattern(node.name)) { @@ -10557,7 +10639,7 @@ module ts { } // It is a SyntaxError if a VariableDeclaration or VariableDeclarationNoIn occurs within strict code // and its Identifier is eval or arguments - return checkGrammarEvalOrArgumentsInStrictMode(node, node.name); + return checkGrammarEvalOrArgumentsInStrictMode(node, node.name); } function checkGrammarVariableDeclarations(container: Node, declarations: NodeArray): boolean { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 8e0d6ce8e98..c35a443db40 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -144,6 +144,7 @@ module ts { Destructuring_declarations_are_not_allowed_in_ambient_contexts: { code: 1183, category: DiagnosticCategory.Error, key: "Destructuring declarations are not allowed in ambient contexts.", isEarly: true }, An_implementation_cannot_be_declared_in_ambient_contexts: { code: 1184, category: DiagnosticCategory.Error, key: "An implementation cannot be declared in ambient contexts.", isEarly: true }, Merge_conflict_marker_encountered: { code: 1184, category: DiagnosticCategory.Error, key: "Merge conflict marker encountered." }, + A_rest_element_cannot_have_an_initializer: { code: 1185, category: DiagnosticCategory.Error, key: "A rest element cannot have an initializer." }, Duplicate_identifier_0: { code: 2300, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'." }, Initializer_of_instance_member_variable_0_cannot_reference_identifier_1_declared_in_the_constructor: { code: 2301, category: DiagnosticCategory.Error, key: "Initializer of instance member variable '{0}' cannot reference identifier '{1}' declared in the constructor." }, Static_members_cannot_reference_class_type_parameters: { code: 2302, category: DiagnosticCategory.Error, key: "Static members cannot reference class type parameters." }, @@ -294,6 +295,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 e5c8ba1a258..466977dee95 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -665,6 +665,10 @@ "category": "Error", "code": 1184 }, + "A rest element cannot have an initializer.": { + "category": "Error", + "code": 1185 + }, "Duplicate identifier '{0}'.": { "category": "Error", @@ -1270,6 +1274,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 0f3e9606815..bb3de86bc7a 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1949,35 +1949,58 @@ module ts { } } + function emitParenthesized(node: Node, parenthesized: boolean) { + if (parenthesized) { + write("("); + } + emit(node); + if (parenthesized) { + write(")"); + } + } + function emitTrailingCommaIfPresent(nodeList: NodeArray): void { if (nodeList.hasTrailingComma) { write(","); } } - function emitCommaList(nodes: Node[], count?: number) { - if (!(count >= 0)) { - count = nodes.length; + function emitList(nodes: Node[], start: number, count: number, multiLine: boolean, trailingComma: boolean) { + if (multiLine) { + increaseIndent(); } - if (nodes) { - for (var i = 0; i < count; i++) { + for (var i = 0; i < count; i++) { + if (multiLine) { + if (i) { + write(","); + } + writeLine(); + } + else { if (i) { write(", "); } - emit(nodes[i]); } + emit(nodes[start + i]); + } + if (trailingComma) { + write(","); + } + if (multiLine) { + decreaseIndent(); + writeLine(); + } + } + + function emitCommaList(nodes: Node[]) { + if (nodes) { + emitList(nodes, 0, nodes.length, /*multiline*/ false, /*trailingComma*/ false); } } function emitMultiLineList(nodes: Node[]) { if (nodes) { - for (var i = 0; i < nodes.length; i++) { - if (i) { - write(","); - } - writeLine(); - emit(nodes[i]); - } + emitList(nodes, 0, nodes.length, /*multiline*/ true, /*trailingComma*/ false); } } @@ -2055,17 +2078,8 @@ module ts { // "abc" + (1 << 2) + "" var needsParens = templateSpan.expression.kind !== SyntaxKind.ParenthesizedExpression && comparePrecedenceToBinaryPlus(templateSpan.expression) !== Comparison.GreaterThan; - write(" + "); - - if (needsParens) { - write("("); - } - emit(templateSpan.expression); - if (needsParens) { - write(")"); - } - + emitParenthesized(templateSpan.expression, needsParens); // Only emit if the literal is non-empty. // The binary '+' operator is left-associative, so the first string concatenation // with the head will force the result up to this point to be a string. @@ -2235,59 +2249,118 @@ module ts { function emitObjectBindingPattern(node: BindingPattern) { write("{ "); - emitCommaList(node.elements); - emitTrailingCommaIfPresent(node.elements); + var elements = node.elements; + emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma); write(" }"); } function emitArrayBindingPattern(node: BindingPattern) { write("["); - emitCommaList(node.elements); - emitTrailingCommaIfPresent(node.elements); + var elements = node.elements; + emitList(elements, 0, elements.length, /*multiLine*/ false, /*trailingComma*/ elements.hasTrailingComma); write("]"); } - function emitArrayLiteral(node: ArrayLiteralExpression) { - if (node.flags & NodeFlags.MultiLine) { - write("["); - increaseIndent(); - emitMultiLineList(node.elements); - emitTrailingCommaIfPresent(node.elements); - decreaseIndent(); - writeLine(); - write("]"); + function emitBindingElement(node: BindingElement) { + if (node.propertyName) { + emit(node.propertyName); + write(": "); + } + if (node.dotDotDotToken) { + write("..."); + } + if (isBindingPattern(node.name)) { + emit(node.name); } else { + emitModuleMemberName(node); + } + emitOptional(" = ", node.initializer); + } + + function emitSpreadElementExpression(node: SpreadElementExpression) { + write("..."); + emit((node).expression); + } + + function needsParenthesisForPropertyAccess(node: Expression) { + switch (node.kind) { + case SyntaxKind.Identifier: + case SyntaxKind.ArrayLiteralExpression: + case SyntaxKind.PropertyAccessExpression: + case SyntaxKind.ElementAccessExpression: + case SyntaxKind.CallExpression: + case SyntaxKind.ParenthesizedExpression: + // This list is not exhaustive and only includes those cases that are relevant + // to the check in emitArrayLiteral. More cases can be added as needed. + return false; + } + return true; + } + + function emitArrayLiteral(node: ArrayLiteralExpression) { + var elements = node.elements; + var length = elements.length; + if (length === 0) { + write("[]"); + return; + } + if (compilerOptions.target >= ScriptTarget.ES6) { write("["); - emitCommaList(node.elements); - emitTrailingCommaIfPresent(node.elements); + emitList(elements, 0, elements.length, /*multiLine*/(node.flags & NodeFlags.MultiLine) !== 0, + /*trailingComma*/ elements.hasTrailingComma); write("]"); + return; + } + var pos = 0; + var group = 0; + while (pos < length) { + // Emit using the pattern .concat(, , ...) + if (group === 1) { + write(".concat("); + } + else if (group > 1) { + write(", "); + } + var e = elements[pos]; + if (e.kind === SyntaxKind.SpreadElementExpression) { + e = (e).expression; + emitParenthesized(e, /*parenthesized*/ group === 0 && needsParenthesisForPropertyAccess(e)); + pos++; + } + else { + var i = pos; + while (i < length && elements[i].kind !== SyntaxKind.SpreadElementExpression) { + i++; + } + write("["); + emitList(elements, pos, i - pos, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0, + /*trailingComma*/ elements.hasTrailingComma); + write("]"); + pos = i; + } + group++; + } + if (group > 1) { + write(")"); } } function emitObjectLiteral(node: ObjectLiteralExpression) { - if (!node.properties.length) { - write("{}"); - } - else if (node.flags & NodeFlags.MultiLine) { - write("{"); - increaseIndent(); - emitMultiLineList(node.properties); - if (compilerOptions.target >= ScriptTarget.ES5) { - emitTrailingCommaIfPresent(node.properties); + write("{"); + var properties = node.properties; + if (properties.length) { + var multiLine = (node.flags & NodeFlags.MultiLine) !== 0; + if (!multiLine) { + write(" "); } - decreaseIndent(); - writeLine(); - write("}"); - } - else { - write("{ "); - emitCommaList(node.properties); - if (compilerOptions.target >= ScriptTarget.ES5) { - emitTrailingCommaIfPresent(node.properties); + emitList(properties, 0, properties.length, /*multiLine*/ multiLine, + /*trailingComma*/ properties.hasTrailingComma && compilerOptions.target >= ScriptTarget.ES5); + if (!multiLine) { + write(" "); } - write(" }"); } + write("}"); } function emitComputedPropertyName(node: ComputedPropertyName) { @@ -2502,7 +2575,8 @@ module ts { function emitBinaryExpression(node: BinaryExpression) { - if (node.operator === SyntaxKind.EqualsToken && (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { + if (compilerOptions.target < ScriptTarget.ES6 && node.operator === SyntaxKind.EqualsToken && + (node.left.kind === SyntaxKind.ObjectLiteralExpression || node.left.kind === SyntaxKind.ArrayLiteralExpression)) { emitDestructuring(node); } else { @@ -2554,11 +2628,8 @@ module ts { } function emitExpressionStatement(node: ExpressionStatement) { - var isArrowExpression = node.expression.kind === SyntaxKind.ArrowFunction; emitLeadingComments(node); - if (isArrowExpression) write("("); - emit(node.expression); - if (isArrowExpression) write(")"); + emitParenthesized(node.expression, /*parenthesized*/ node.expression.kind === SyntaxKind.ArrowFunction); write(";"); emitTrailingComments(node); } @@ -2899,7 +2970,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 + ")"); + } + } } } } @@ -2965,8 +3045,17 @@ module ts { emitBindingElement(element, createPropertyAccess(value, propName)); } else if (element.kind !== SyntaxKind.OmittedExpression) { - // Rewrite element to a declaration that accesses array element at index i - emitBindingElement(element, createElementAccess(value, createNumericLiteral(i))); + if (!element.dotDotDotToken) { + // Rewrite element to a declaration that accesses array element at index i + emitBindingElement(element, createElementAccess(value, createNumericLiteral(i))); + } + else { + if (i === elements.length - 1) { + value = ensureIdentifier(value); + emitAssignment(element.name, value); + write(".slice(" + i + ")"); + } + } } } } @@ -2979,7 +3068,13 @@ module ts { function emitVariableDeclaration(node: VariableDeclaration) { emitLeadingComments(node); if (isBindingPattern(node.name)) { - emitDestructuring(node); + if (compilerOptions.target < ScriptTarget.ES6) { + emitDestructuring(node); + } + else { + emit(node.name); + emitOptional(" = ", node.initializer); + } } else { emitModuleMemberName(node); @@ -3008,57 +3103,61 @@ module ts { function emitParameter(node: ParameterDeclaration) { emitLeadingComments(node); - if (isBindingPattern(node.name)) { - var name = createTempVariable(node); - if (!tempParameters) { - tempParameters = []; + if (compilerOptions.target < ScriptTarget.ES6) { + if (isBindingPattern(node.name)) { + var name = createTempVariable(node); + if (!tempParameters) { + tempParameters = []; + } + tempParameters.push(name); + emit(name); + } + else { + emit(node.name); } - tempParameters.push(name); - emit(name); } else { + if (node.dotDotDotToken) { + write("..."); + } emit(node.name); + emitOptional(" = ", node.initializer); } - // TODO(andersh): Enable ES6 code generation below - //if (node.propertyName) { - // emit(node.propertyName); - // write(": "); - //} - //emit(node.name); - //emitOptional(" = ", node.initializer); emitTrailingComments(node); } function emitDefaultValueAssignments(node: FunctionLikeDeclaration) { - var tempIndex = 0; - forEach(node.parameters, p => { - if (isBindingPattern(p.name)) { - writeLine(); - write("var "); - emitDestructuring(p, tempParameters[tempIndex]); - write(";"); - tempIndex++; - } - else if (p.initializer) { - writeLine(); - emitStart(p); - write("if ("); - emitNode(p.name); - write(" === void 0)"); - emitEnd(p); - write(" { "); - emitStart(p); - emitNode(p.name); - write(" = "); - emitNode(p.initializer); - emitEnd(p); - write("; }"); - } - }); + if (compilerOptions.target < ScriptTarget.ES6) { + var tempIndex = 0; + forEach(node.parameters, p => { + if (isBindingPattern(p.name)) { + writeLine(); + write("var "); + emitDestructuring(p, tempParameters[tempIndex]); + write(";"); + tempIndex++; + } + else if (p.initializer) { + writeLine(); + emitStart(p); + write("if ("); + emitNode(p.name); + write(" === void 0)"); + emitEnd(p); + write(" { "); + emitStart(p); + emitNode(p.name); + write(" = "); + emitNode(p.initializer); + emitEnd(p); + write("; }"); + } + }); + } } function emitRestParameter(node: FunctionLikeDeclaration) { - if (hasRestParameters(node)) { + if (compilerOptions.target < ScriptTarget.ES6 && hasRestParameters(node)) { var restIndex = node.parameters.length - 1; var restParam = node.parameters[restIndex]; var tempName = createTempVariable(node, /*forLoopVariable*/ true).text; @@ -3136,7 +3235,9 @@ module ts { increaseIndent(); write("("); if (node) { - emitCommaList(node.parameters, node.parameters.length - (hasRestParameters(node) ? 1 : 0)); + var parameters = node.parameters; + var omitCount = compilerOptions.target < ScriptTarget.ES6 && hasRestParameters(node) ? 1 : 0; + emitList(parameters, 0, parameters.length - omitCount, /*multiLine*/ false, /*trailingComma*/ false); } write(")"); decreaseIndent(); @@ -3723,17 +3824,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(); @@ -3743,17 +3845,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); } } @@ -3809,6 +3912,7 @@ module ts { else { emitCaptureThisForNodeIfNecessary(node); emitLinesStartingAt(node.statements, startIndex); + emitTempDeclarations(/*newLine*/ true); } emitLeadingComments(node.endOfFileToken); @@ -3861,6 +3965,8 @@ module ts { return emitObjectBindingPattern(node); case SyntaxKind.ArrayBindingPattern: return emitArrayBindingPattern(node); + case SyntaxKind.BindingElement: + return emitBindingElement(node); case SyntaxKind.ArrayLiteralExpression: return emitArrayLiteral(node); case SyntaxKind.ObjectLiteralExpression: @@ -3903,6 +4009,8 @@ module ts { return emitBinaryExpression(node); case SyntaxKind.ConditionalExpression: return emitConditionalExpression(node); + case SyntaxKind.SpreadElementExpression: + return emitSpreadElementExpression(node); case SyntaxKind.OmittedExpression: return; case SyntaxKind.Block: diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1cee0039568..b3d4cb5614c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -151,6 +151,8 @@ module ts { return child((node).condition) || child((node).whenTrue) || child((node).whenFalse); + case SyntaxKind.SpreadElementExpression: + return child((node).expression); case SyntaxKind.Block: case SyntaxKind.TryBlock: case SyntaxKind.FinallyBlock: @@ -1005,13 +1007,13 @@ module ts { case ParsingContext.VariableDeclarations: return isIdentifierOrPattern(); case ParsingContext.ArrayBindingElements: - return token === SyntaxKind.CommaToken || isIdentifierOrPattern(); + return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isIdentifierOrPattern(); case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: return token === SyntaxKind.CommaToken || isStartOfExpression(); case ParsingContext.ArrayLiteralMembers: - return token === SyntaxKind.CommaToken || isStartOfExpression(); + return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isStartOfExpression(); case ParsingContext.Parameters: return isStartOfParameter(); case ParsingContext.TypeArguments: @@ -2777,8 +2779,15 @@ module ts { : parseAssignmentExpressionOrHigher(); } + function parseSpreadElement(): Expression { + var node = createNode(SyntaxKind.SpreadElementExpression); + parseExpected(SyntaxKind.DotDotDotToken); + node.expression = parseAssignmentExpressionOrHigher(); + return finishNode(node); + } + function parseArrayLiteralElement(): Expression { - return parseAssignmentExpressionOrOmittedExpression(); + return token === SyntaxKind.DotDotDotToken ? parseSpreadElement() : parseAssignmentExpressionOrOmittedExpression(); } function parseArgumentExpression(): Expression { @@ -3281,8 +3290,8 @@ module ts { if (token === SyntaxKind.CommaToken) { return createNode(SyntaxKind.OmittedExpression); } - var node = createNode(SyntaxKind.BindingElement); + node.dotDotDotToken = parseOptionalToken(SyntaxKind.DotDotDotToken); node.name = parseIdentifierOrPattern(); node.initializer = parseInitializer(/*inParameter*/ false); return finishNode(node); @@ -3290,7 +3299,6 @@ module ts { function parseObjectBindingElement(): BindingElement { var node = createNode(SyntaxKind.BindingElement); - // TODO(andersh): Handle computed properties var id = parsePropertyName(); if (id.kind === SyntaxKind.Identifier && token !== SyntaxKind.ColonToken) { @@ -3301,7 +3309,6 @@ module ts { node.propertyName = id; node.name = parseIdentifierOrPattern(); } - node.initializer = parseInitializer(/*inParameter*/ false); return finishNode(node); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 50cc2e5658c..f3c87ceb689 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -196,6 +196,7 @@ module ts { ConditionalExpression, TemplateExpression, YieldExpression, + SpreadElementExpression, OmittedExpression, // Misc TemplateSpan, @@ -659,7 +660,11 @@ module ts { export interface ArrayLiteralExpression extends PrimaryExpression { elements: NodeArray; } - + + export interface SpreadElementExpression extends Expression { + expression: Expression; + } + // An ObjectLiteralExpression is the declaration node for an anonymous symbol. export interface ObjectLiteralExpression extends PrimaryExpression, Declaration { properties: NodeArray; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 8e0a02b2be6..4c57cf2cf06 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -415,6 +415,7 @@ module ts { case SyntaxKind.PostfixUnaryExpression: case SyntaxKind.BinaryExpression: case SyntaxKind.ConditionalExpression: + case SyntaxKind.SpreadElementExpression: case SyntaxKind.TemplateExpression: case SyntaxKind.NoSubstitutionTemplateLiteral: case SyntaxKind.OmittedExpression: diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 624b73c68a9..174b311e763 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -133,7 +133,7 @@ module FourSlash { testOptMetadataNames.outDir, testOptMetadataNames.sourceMap, testOptMetadataNames.sourceRoot] function convertGlobalOptionsToCompilerOptions(globalOptions: { [idx: string]: string }): ts.CompilerOptions { - var settings: ts.CompilerOptions = {}; + var settings: ts.CompilerOptions = { target: ts.ScriptTarget.ES5 }; // Convert all property in globalOptions into ts.CompilationSettings for (var prop in globalOptions) { if (globalOptions.hasOwnProperty(prop)) { @@ -2590,4 +2590,4 @@ module FourSlash { fileName: fileName }; } -} \ No newline at end of file +} diff --git a/tests/baselines/reference/arrayLiteralSpread.js b/tests/baselines/reference/arrayLiteralSpread.js new file mode 100644 index 00000000000..73a60714526 --- /dev/null +++ b/tests/baselines/reference/arrayLiteralSpread.js @@ -0,0 +1,46 @@ +//// [arrayLiteralSpread.ts] +function f0() { + var a = [1, 2, 3]; + var a1 = [...a]; + var a2 = [1, ...a]; + var a3 = [1, 2, ...a]; + var a4 = [...a, 1]; + var a5 = [...a, 1, 2]; + var a6 = [1, 2, ...a, 1, 2]; + var a7 = [1, ...a, 2, ...a]; + var a8 = [...a, ...a, ...a]; +} + +function f1() { + var a = [1, 2, 3]; + var b = ["hello", ...a, true]; + var b: (string | number | boolean)[]; +} + +function f2() { + var a = [...[...[...[...[...[]]]]]]; + var b = [...[...[...[...[...[5]]]]]]; +} + + +//// [arrayLiteralSpread.js] +function f0() { + var a = [1, 2, 3]; + var a1 = a; + var a2 = [1].concat(a); + var a3 = [1, 2].concat(a); + var a4 = a.concat([1]); + var a5 = a.concat([1, 2]); + var a6 = [1, 2].concat(a, [1, 2]); + var a7 = [1].concat(a, [2], a); + var a8 = a.concat(a, a); +} +function f1() { + var a = [1, 2, 3]; + var b = ["hello"].concat(a, [true]); + var b; +} +function f2() { + var a = []; + var b = [5]; +} diff --git a/tests/baselines/reference/arrayLiteralSpread.types b/tests/baselines/reference/arrayLiteralSpread.types new file mode 100644 index 00000000000..0dae59521bf --- /dev/null +++ b/tests/baselines/reference/arrayLiteralSpread.types @@ -0,0 +1,90 @@ +=== tests/cases/conformance/es6/spread/arrayLiteralSpread.ts === +function f0() { +>f0 : () => void + + var a = [1, 2, 3]; +>a : number[] +>[1, 2, 3] : number[] + + var a1 = [...a]; +>a1 : number[] +>[...a] : number[] +>a : number[] + + var a2 = [1, ...a]; +>a2 : number[] +>[1, ...a] : number[] +>a : number[] + + var a3 = [1, 2, ...a]; +>a3 : number[] +>[1, 2, ...a] : number[] +>a : number[] + + var a4 = [...a, 1]; +>a4 : number[] +>[...a, 1] : number[] +>a : number[] + + var a5 = [...a, 1, 2]; +>a5 : number[] +>[...a, 1, 2] : number[] +>a : number[] + + var a6 = [1, 2, ...a, 1, 2]; +>a6 : number[] +>[1, 2, ...a, 1, 2] : number[] +>a : number[] + + var a7 = [1, ...a, 2, ...a]; +>a7 : number[] +>[1, ...a, 2, ...a] : number[] +>a : number[] +>a : number[] + + var a8 = [...a, ...a, ...a]; +>a8 : number[] +>[...a, ...a, ...a] : number[] +>a : number[] +>a : number[] +>a : number[] +} + +function f1() { +>f1 : () => void + + var a = [1, 2, 3]; +>a : number[] +>[1, 2, 3] : number[] + + var b = ["hello", ...a, true]; +>b : (string | number | boolean)[] +>["hello", ...a, true] : (string | number | boolean)[] +>a : number[] + + var b: (string | number | boolean)[]; +>b : (string | number | boolean)[] +} + +function f2() { +>f2 : () => void + + var a = [...[...[...[...[...[]]]]]]; +>a : any[] +>[...[...[...[...[...[]]]]]] : undefined[] +>[...[...[...[...[]]]]] : undefined[] +>[...[...[...[]]]] : undefined[] +>[...[...[]]] : undefined[] +>[...[]] : undefined[] +>[] : undefined[] + + var b = [...[...[...[...[...[5]]]]]]; +>b : number[] +>[...[...[...[...[...[5]]]]]] : number[] +>[...[...[...[...[5]]]]] : number[] +>[...[...[...[5]]]] : number[] +>[...[...[5]]] : number[] +>[...[5]] : number[] +>[5] : number[] +} + diff --git a/tests/baselines/reference/declarationsAndAssignments.errors.txt b/tests/baselines/reference/declarationsAndAssignments.errors.txt index 2c9a54c869c..4eefc2fcc23 100644 --- a/tests/baselines/reference/declarationsAndAssignments.errors.txt +++ b/tests/baselines/reference/declarationsAndAssignments.errors.txt @@ -176,7 +176,7 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): f17({ c: true }); f17(f15()); - function g4() { + function f18() { var a: number; var b: string; var aa: number[]; @@ -191,7 +191,7 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): [a = 1, b = "abc"] = [2, "def"]; } - function g5() { + function f19() { var a, b; [a, b] = [1, 2]; [a, b] = [b, a]; @@ -199,4 +199,34 @@ tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts(138,9): [[a, b] = [1, 2]] = [[2, 3]]; var x = ([a, b] = [1, 2]); } + + function f20() { + var a: number[]; + var x: number; + var y: number; + var z: number; + var [...a] = [1, 2, 3]; + var [x, ...a] = [1, 2, 3]; + var [x, y, ...a] = [1, 2, 3]; + var [x, y, z, ...a] = [1, 2, 3]; + [...a] = [1, 2, 3]; + [x, ...a] = [1, 2, 3]; + [x, y, ...a] = [1, 2, 3]; + [x, y, z, ...a] = [1, 2, 3]; + } + + function f21() { + var a: (number | string | boolean)[]; + var x: number | string | boolean; + var y: number | string | boolean; + var z: number | string | boolean; + var [...a] = [1, "hello", true]; + var [x, ...a] = [1, "hello", true]; + var [x, y, ...a] = [1, "hello", true]; + var [x, y, z, ...a] = [1, "hello", true]; + [...a] = [1, "hello", true]; + [x, ...a] = [1, "hello", true]; + [x, y, ...a] = [1, "hello", true]; + [x, y, z, ...a] = [1, "hello", true]; + } \ No newline at end of file diff --git a/tests/baselines/reference/declarationsAndAssignments.js b/tests/baselines/reference/declarationsAndAssignments.js index ba2e74dc420..032f4cb0e63 100644 --- a/tests/baselines/reference/declarationsAndAssignments.js +++ b/tests/baselines/reference/declarationsAndAssignments.js @@ -129,7 +129,7 @@ f17({ a: "hello" }); f17({ c: true }); f17(f15()); -function g4() { +function f18() { var a: number; var b: string; var aa: number[]; @@ -140,7 +140,7 @@ function g4() { [a = 1, b = "abc"] = [2, "def"]; } -function g5() { +function f19() { var a, b; [a, b] = [1, 2]; [a, b] = [b, a]; @@ -148,6 +148,36 @@ function g5() { [[a, b] = [1, 2]] = [[2, 3]]; var x = ([a, b] = [1, 2]); } + +function f20() { + var a: number[]; + var x: number; + var y: number; + var z: number; + var [...a] = [1, 2, 3]; + var [x, ...a] = [1, 2, 3]; + var [x, y, ...a] = [1, 2, 3]; + var [x, y, z, ...a] = [1, 2, 3]; + [...a] = [1, 2, 3]; + [x, ...a] = [1, 2, 3]; + [x, y, ...a] = [1, 2, 3]; + [x, y, z, ...a] = [1, 2, 3]; +} + +function f21() { + var a: (number | string | boolean)[]; + var x: number | string | boolean; + var y: number | string | boolean; + var z: number | string | boolean; + var [...a] = [1, "hello", true]; + var [x, ...a] = [1, "hello", true]; + var [x, y, ...a] = [1, "hello", true]; + var [x, y, z, ...a] = [1, "hello", true]; + [...a] = [1, "hello", true]; + [x, ...a] = [1, "hello", true]; + [x, y, ...a] = [1, "hello", true]; + [x, y, z, ...a] = [1, "hello", true]; +} //// [declarationsAndAssignments.js] @@ -266,7 +296,7 @@ f17({}); f17({ a: "hello" }); f17({ c: true }); f17(f15()); -function g4() { +function f18() { var a; var b; var aa; @@ -277,7 +307,7 @@ function g4() { _e = [2, "def"], _f = _e[0], a = _f === void 0 ? 1 : _f, _g = _e[1], b = _g === void 0 ? "abc" : _g; var _a, _b, _c, _d, _e, _f, _g; } -function g5() { +function f19() { var a, b; _a = [1, 2], a = _a[0], b = _a[1]; _b = [b, a], a = _b[0], b = _b[1]; @@ -286,3 +316,33 @@ function g5() { var x = (_f = [1, 2], a = _f[0], b = _f[1], _f); var _a, _b, _c, _d, _e, _f; } +function f20() { + var a; + var x; + var y; + var z; + var _a = [1, 2, 3], a = _a.slice(0); + var _b = [1, 2, 3], x = _b[0], a = _b.slice(1); + var _c = [1, 2, 3], x = _c[0], y = _c[1], a = _c.slice(2); + var _d = [1, 2, 3], x = _d[0], y = _d[1], z = _d[2], a = _d.slice(3); + _e = [1, 2, 3], a = _e.slice(0); + _f = [1, 2, 3], x = _f[0], a = _f.slice(1); + _g = [1, 2, 3], x = _g[0], y = _g[1], a = _g.slice(2); + _h = [1, 2, 3], x = _h[0], y = _h[1], z = _h[2], a = _h.slice(3); + var _e, _f, _g, _h; +} +function f21() { + var a; + var x; + var y; + var z; + var _a = [1, "hello", true], a = _a.slice(0); + var _b = [1, "hello", true], x = _b[0], a = _b.slice(1); + var _c = [1, "hello", true], x = _c[0], y = _c[1], a = _c.slice(2); + var _d = [1, "hello", true], x = _d[0], y = _d[1], z = _d[2], a = _d.slice(3); + _e = [1, "hello", true], a = _e.slice(0); + _f = [1, "hello", true], x = _f[0], a = _f.slice(1); + _g = [1, "hello", true], x = _g[0], y = _g[1], a = _g.slice(2); + _h = [1, "hello", true], x = _h[0], y = _h[1], z = _h[2], a = _h.slice(3); + var _e, _f, _g, _h; +} diff --git a/tests/baselines/reference/restElementMustBeLast.errors.txt b/tests/baselines/reference/restElementMustBeLast.errors.txt new file mode 100644 index 00000000000..269d984bc00 --- /dev/null +++ b/tests/baselines/reference/restElementMustBeLast.errors.txt @@ -0,0 +1,12 @@ +tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts(1,9): error TS2462: A rest element must be last in an array destructuring pattern +tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts(2,2): error TS2462: A rest element must be last in an array destructuring pattern + + +==== tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts (2 errors) ==== + var [...a, x] = [1, 2, 3]; // Error, rest must be last element + ~ +!!! error TS2462: A rest element must be last in an array destructuring pattern + [...a, x] = [1, 2, 3]; // Error, rest must be last element + ~~~~ +!!! error TS2462: A rest element must be last in an array destructuring pattern + \ No newline at end of file diff --git a/tests/baselines/reference/restElementMustBeLast.js b/tests/baselines/reference/restElementMustBeLast.js new file mode 100644 index 00000000000..337cb6de6f6 --- /dev/null +++ b/tests/baselines/reference/restElementMustBeLast.js @@ -0,0 +1,9 @@ +//// [restElementMustBeLast.ts] +var [...a, x] = [1, 2, 3]; // Error, rest must be last element +[...a, x] = [1, 2, 3]; // Error, rest must be last element + + +//// [restElementMustBeLast.js] +var _a = [1, 2, 3], x = _a[1]; // Error, rest must be last element +_b = [1, 2, 3], x = _b[1]; // Error, rest must be last element +var _b; diff --git a/tests/baselines/reference/restElementWithInitializer1.errors.txt b/tests/baselines/reference/restElementWithInitializer1.errors.txt new file mode 100644 index 00000000000..6cee9d5286a --- /dev/null +++ b/tests/baselines/reference/restElementWithInitializer1.errors.txt @@ -0,0 +1,9 @@ +tests/cases/conformance/es6/destructuring/restElementWithInitializer1.ts(2,11): error TS1185: A rest element cannot have an initializer. + + +==== tests/cases/conformance/es6/destructuring/restElementWithInitializer1.ts (1 errors) ==== + var a: number[]; + var [...x = a] = a; // Error, rest element cannot have initializer + ~ +!!! error TS1185: A rest element cannot have an initializer. + \ No newline at end of file diff --git a/tests/baselines/reference/restElementWithInitializer1.js b/tests/baselines/reference/restElementWithInitializer1.js new file mode 100644 index 00000000000..26df58e3cf3 --- /dev/null +++ b/tests/baselines/reference/restElementWithInitializer1.js @@ -0,0 +1,8 @@ +//// [restElementWithInitializer1.ts] +var a: number[]; +var [...x = a] = a; // Error, rest element cannot have initializer + + +//// [restElementWithInitializer1.js] +var a; +var x = a.slice(0); // Error, rest element cannot have initializer diff --git a/tests/baselines/reference/restElementWithInitializer2.errors.txt b/tests/baselines/reference/restElementWithInitializer2.errors.txt new file mode 100644 index 00000000000..9c4e95e3963 --- /dev/null +++ b/tests/baselines/reference/restElementWithInitializer2.errors.txt @@ -0,0 +1,10 @@ +tests/cases/conformance/es6/destructuring/restElementWithInitializer2.ts(3,5): error TS2364: Invalid left-hand side of assignment expression. + + +==== tests/cases/conformance/es6/destructuring/restElementWithInitializer2.ts (1 errors) ==== + var a: number[]; + var x: number[]; + [...x = a] = a; // Error, rest element cannot have initializer + ~~~~~ +!!! error TS2364: Invalid left-hand side of assignment expression. + \ No newline at end of file diff --git a/tests/baselines/reference/restElementWithInitializer2.js b/tests/baselines/reference/restElementWithInitializer2.js new file mode 100644 index 00000000000..8874f6a83d0 --- /dev/null +++ b/tests/baselines/reference/restElementWithInitializer2.js @@ -0,0 +1,10 @@ +//// [restElementWithInitializer2.ts] +var a: number[]; +var x: number[]; +[...x = a] = a; // Error, rest element cannot have initializer + + +//// [restElementWithInitializer2.js] +var a; +var x; +x = a = a.slice(0); // Error, rest element cannot have initializer diff --git a/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution1_ES6.js b/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution1_ES6.js index a2a96168fd8..bd121933b32 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution1_ES6.js +++ b/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution1_ES6.js @@ -23,11 +23,7 @@ var z = foo `${1}${2}${3}`; // any (with error) //// [taggedTemplateStringsWithOverloadResolution1_ES6.js] -function foo() { - var stuff = []; - for (var _i = 0; _i < arguments.length; _i++) { - stuff[_i - 0] = arguments[_i]; - } +function foo(...stuff) { return undefined; } var a = foo([]); // number diff --git a/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution2_ES6.js b/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution2_ES6.js index 44da93f8041..f208a63354d 100644 --- a/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution2_ES6.js +++ b/tests/baselines/reference/taggedTemplateStringsWithOverloadResolution2_ES6.js @@ -18,20 +18,12 @@ var c = foo2 `${1}`; // number var d = foo2([], 1); // number //// [taggedTemplateStringsWithOverloadResolution2_ES6.js] -function foo1() { - var stuff = []; - for (var _i = 0; _i < arguments.length; _i++) { - stuff[_i - 0] = arguments[_i]; - } +function foo1(...stuff) { return undefined; } var a = foo1 `${1}`; // string var b = foo1([], 1); // number -function foo2() { - var stuff = []; - for (var _i = 0; _i < arguments.length; _i++) { - stuff[_i - 0] = arguments[_i]; - } +function foo2(...stuff) { return undefined; } var c = foo2 `${1}`; // number diff --git a/tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts b/tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts index 04cdd153a57..2202a482f8b 100644 --- a/tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts +++ b/tests/cases/conformance/es6/destructuring/declarationsAndAssignments.ts @@ -128,7 +128,7 @@ f17({ a: "hello" }); f17({ c: true }); f17(f15()); -function g4() { +function f18() { var a: number; var b: string; var aa: number[]; @@ -139,7 +139,7 @@ function g4() { [a = 1, b = "abc"] = [2, "def"]; } -function g5() { +function f19() { var a, b; [a, b] = [1, 2]; [a, b] = [b, a]; @@ -147,3 +147,33 @@ function g5() { [[a, b] = [1, 2]] = [[2, 3]]; var x = ([a, b] = [1, 2]); } + +function f20() { + var a: number[]; + var x: number; + var y: number; + var z: number; + var [...a] = [1, 2, 3]; + var [x, ...a] = [1, 2, 3]; + var [x, y, ...a] = [1, 2, 3]; + var [x, y, z, ...a] = [1, 2, 3]; + [...a] = [1, 2, 3]; + [x, ...a] = [1, 2, 3]; + [x, y, ...a] = [1, 2, 3]; + [x, y, z, ...a] = [1, 2, 3]; +} + +function f21() { + var a: (number | string | boolean)[]; + var x: number | string | boolean; + var y: number | string | boolean; + var z: number | string | boolean; + var [...a] = [1, "hello", true]; + var [x, ...a] = [1, "hello", true]; + var [x, y, ...a] = [1, "hello", true]; + var [x, y, z, ...a] = [1, "hello", true]; + [...a] = [1, "hello", true]; + [x, ...a] = [1, "hello", true]; + [x, y, ...a] = [1, "hello", true]; + [x, y, z, ...a] = [1, "hello", true]; +} diff --git a/tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts b/tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts new file mode 100644 index 00000000000..6e7b91033a1 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/restElementMustBeLast.ts @@ -0,0 +1,2 @@ +var [...a, x] = [1, 2, 3]; // Error, rest must be last element +[...a, x] = [1, 2, 3]; // Error, rest must be last element diff --git a/tests/cases/conformance/es6/destructuring/restElementWithInitializer1.ts b/tests/cases/conformance/es6/destructuring/restElementWithInitializer1.ts new file mode 100644 index 00000000000..7357e36cc05 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/restElementWithInitializer1.ts @@ -0,0 +1,2 @@ +var a: number[]; +var [...x = a] = a; // Error, rest element cannot have initializer diff --git a/tests/cases/conformance/es6/destructuring/restElementWithInitializer2.ts b/tests/cases/conformance/es6/destructuring/restElementWithInitializer2.ts new file mode 100644 index 00000000000..dc41aacf0b9 --- /dev/null +++ b/tests/cases/conformance/es6/destructuring/restElementWithInitializer2.ts @@ -0,0 +1,3 @@ +var a: number[]; +var x: number[]; +[...x = a] = a; // Error, rest element cannot have initializer diff --git a/tests/cases/conformance/es6/spread/arrayLiteralSpread.ts b/tests/cases/conformance/es6/spread/arrayLiteralSpread.ts new file mode 100644 index 00000000000..da088cc09a0 --- /dev/null +++ b/tests/cases/conformance/es6/spread/arrayLiteralSpread.ts @@ -0,0 +1,22 @@ +function f0() { + var a = [1, 2, 3]; + var a1 = [...a]; + var a2 = [1, ...a]; + var a3 = [1, 2, ...a]; + var a4 = [...a, 1]; + var a5 = [...a, 1, 2]; + var a6 = [1, 2, ...a, 1, 2]; + var a7 = [1, ...a, 2, ...a]; + var a8 = [...a, ...a, ...a]; +} + +function f1() { + var a = [1, 2, 3]; + var b = ["hello", ...a, true]; + var b: (string | number | boolean)[]; +} + +function f2() { + var a = [...[...[...[...[...[]]]]]]; + var b = [...[...[...[...[...[5]]]]]]; +}