diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7c43b8bb2337c..39dd49176b601 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5822,10 +5822,74 @@ module ts { return unknownSignature; } + // Re-order candidate signatures into the result array. Assumes the result array to be empty. + // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order + // A nit here is that we reorder only signatures that belong to the same symbol, + // so order how inherited signatures are processed is still preserved. + // interface A { (x: string): void } + // interface B extends A { (x: 'foo'): string } + // var b: B; + // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] + function reorderCandidates(signatures: Signature[], result: Signature[]): void { + var lastParent: Node; + var lastSymbol: Symbol; + var cutoffIndex: number = 0; + var index: number; + var specializedIndex: number = -1; + var spliceIndex: number; + Debug.assert(!result.length); + for (var i = 0; i < signatures.length; i++) { + var signature = signatures[i]; + var symbol = signature.declaration && getSymbolOfNode(signature.declaration); + var parent = signature.declaration && signature.declaration.parent; + if (!lastSymbol || symbol === lastSymbol) { + if (lastParent && parent === lastParent) { + index++; + } + else { + lastParent = parent; + index = cutoffIndex; + } + } + else { + // current declaration belongs to a different symbol + // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex + index = cutoffIndex = result.length; + lastParent = parent; + } + lastSymbol = symbol; + + // specialized signatures always need to be placed before non-specialized signatures regardless + // of the cutoff position; see GH#1133 + if (signature.hasStringLiterals) { + specializedIndex++; + spliceIndex = specializedIndex; + // The cutoff index always needs to be greater than or equal to the specialized signature index + // in order to prevent non-specialized signatures from being added before a specialized + // signature. + cutoffIndex++; + } + else { + spliceIndex = index; + } + + result.splice(spliceIndex, 0, signature); + } + } + + function getSpreadArgumentIndex(args: Expression[]): number { + for (var i = 0; i < args.length; i++) { + if (args[i].kind === SyntaxKind.SpreadElementExpression) { + return i; + } + } + return -1; + } + function hasCorrectArity(node: CallLikeExpression, args: Expression[], signature: Signature) { - var adjustedArgCount: number; - var typeArguments: NodeArray; - var callIsIncomplete: boolean; + var adjustedArgCount: number; // Apparent number of arguments we will have in this call + var typeArguments: NodeArray; // Type arguments (undefined if none) + var callIsIncomplete: boolean; // In incomplete call we want to be lenient when we have too few arguments if (node.kind === SyntaxKind.TaggedTemplateExpression) { var tagExpression = node; @@ -5870,35 +5934,29 @@ module ts { typeArguments = callExpression.typeArguments; } - Debug.assert(adjustedArgCount !== undefined, "'adjustedArgCount' undefined"); - Debug.assert(callIsIncomplete !== undefined, "'callIsIncomplete' undefined"); - - return checkArity(adjustedArgCount, typeArguments, callIsIncomplete, signature); - - /** - * @param adjustedArgCount The "apparent" number of arguments that we will have in this call. - * @param typeArguments Type arguments node of the call if it exists; undefined otherwise. - * @param callIsIncomplete Whether or not a call is unfinished, and we should be "lenient" when we have too few arguments. - * @param signature The signature whose arity we are comparing. - */ - function checkArity(adjustedArgCount: number, typeArguments: NodeArray, callIsIncomplete: boolean, signature: Signature): boolean { - // Too many arguments implies incorrect arity. - if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) { - return false; - } + // If the user supplied type arguments, but the number of type arguments does not match + // the declared number of type parameters, the call has an incorrect arity. + var hasRightNumberOfTypeArgs = !typeArguments || + (signature.typeParameters && typeArguments.length === signature.typeParameters.length); + if (!hasRightNumberOfTypeArgs) { + return false; + } - // If the user supplied type arguments, but the number of type arguments does not match - // the declared number of type parameters, the call has an incorrect arity. - var hasRightNumberOfTypeArgs = !typeArguments || - (signature.typeParameters && typeArguments.length === signature.typeParameters.length); - if (!hasRightNumberOfTypeArgs) { - return false; - } + // If spread arguments are present, check that they correspond to a rest parameter. If so, no + // further checking is necessary. + var spreadArgIndex = getSpreadArgumentIndex(args); + if (spreadArgIndex >= 0) { + return signature.hasRestParameter && spreadArgIndex >= signature.parameters.length - 1; + } - // If the call is incomplete, we should skip the lower bound check. - var hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount; - return callIsIncomplete || hasEnoughArguments; + // Too many arguments implies incorrect arity. + if (!signature.hasRestParameter && adjustedArgCount > signature.parameters.length) { + return false; } + + // If the call is incomplete, we should skip the lower bound check. + var hasEnoughArguments = adjustedArgCount >= signature.minArgumentCount; + return callIsIncomplete || hasEnoughArguments; } // If type has a single call signature and no other members, return that signature. Otherwise, return undefined. @@ -5931,18 +5989,20 @@ module ts { // We perform two passes over the arguments. In the first pass we infer from all arguments, but use // wildcards for all context sensitive function expressions. for (var i = 0; i < args.length; i++) { - if (args[i].kind === SyntaxKind.OmittedExpression) { - continue; - } - var parameterType = getTypeAtPosition(signature, i); - if (i === 0 && args[i].parent.kind === SyntaxKind.TaggedTemplateExpression) { - inferTypes(context, globalTemplateStringsArrayType, parameterType); - continue; + var arg = args[i]; + if (arg.kind !== SyntaxKind.OmittedExpression) { + var paramType = getTypeAtPosition(signature, arg.kind === SyntaxKind.SpreadElementExpression ? -1 : i); + if (i === 0 && args[i].parent.kind === SyntaxKind.TaggedTemplateExpression) { + var argType = globalTemplateStringsArrayType; + } + else { + // For context sensitive arguments we pass the identityMapper, which is a signal to treat all + // context sensitive function expressions as wildcards + var mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : inferenceMapper; + var argType = checkExpressionWithContextualType(arg, paramType, mapper); + } + inferTypes(context, argType, paramType); } - // For context sensitive arguments we pass the identityMapper, which is a signal to treat all - // context sensitive function expressions as wildcards - var mapper = excludeArgument && excludeArgument[i] !== undefined ? identityMapper : inferenceMapper; - inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, mapper), parameterType); } // In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this @@ -5950,13 +6010,11 @@ module ts { // as we construct types for contextually typed parameters) if (excludeArgument) { for (var i = 0; i < args.length; i++) { - if (args[i].kind === SyntaxKind.OmittedExpression) { - continue; - } - // No need to special-case tagged templates; their excludeArgument value will be 'undefined'. + // No need to check for omitted args and template expressions, their exlusion value is always undefined if (excludeArgument[i] === false) { - var parameterType = getTypeAtPosition(signature, i); - inferTypes(context, checkExpressionWithContextualType(args[i], parameterType, inferenceMapper), parameterType); + var arg = args[i]; + var paramType = getTypeAtPosition(signature, arg.kind === SyntaxKind.SpreadElementExpression ? -1 : i); + inferTypes(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType); } } } @@ -5994,37 +6052,24 @@ module ts { return typeArgumentsAreAssignable; } - function checkApplicableSignature(node: CallLikeExpression, args: Node[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { + function checkApplicableSignature(node: CallLikeExpression, args: Expression[], signature: Signature, relation: Map, excludeArgument: boolean[], reportErrors: boolean) { for (var i = 0; i < args.length; i++) { var arg = args[i]; - var argType: Type; - - if (arg.kind === SyntaxKind.OmittedExpression) { - continue; - } - - var paramType = getTypeAtPosition(signature, i); - - if (i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression) { - // A tagged template expression has something of a - // "virtual" parameter with the "cooked" strings array type. - argType = globalTemplateStringsArrayType; - } - else { - // String literals get string literal types unless we're reporting errors - argType = arg.kind === SyntaxKind.StringLiteral && !reportErrors - ? getStringLiteralType(arg) - : checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); - } - - // Use argument expression as error location when reporting errors - var isValidArgument = checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined, - Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1); - if (!isValidArgument) { - return false; + if (arg.kind !== SyntaxKind.OmittedExpression) { + // Check spread elements against rest type (from arity check we know spread argument corresponds to a rest parameter) + var paramType = getTypeAtPosition(signature, arg.kind === SyntaxKind.SpreadElementExpression ? -1 : i); + // A tagged template expression provides a special first argument, and string literals get string literal types + // unless we're reporting errors + var argType = i === 0 && node.kind === SyntaxKind.TaggedTemplateExpression ? globalTemplateStringsArrayType : + arg.kind === SyntaxKind.StringLiteral && !reportErrors ? getStringLiteralType(arg) : + checkExpressionWithContextualType(arg, paramType, excludeArgument && excludeArgument[i] ? identityMapper : undefined); + // Use argument expression as error location when reporting errors + if (!checkTypeRelatedTo(argType, paramType, relation, reportErrors ? arg : undefined, + Diagnostics.Argument_of_type_0_is_not_assignable_to_parameter_of_type_1)) { + return false; + } } } - return true; } @@ -6091,8 +6136,8 @@ module ts { } var candidates = candidatesOutArray || []; - // collectCandidates fills up the candidates array directly - collectCandidates(); + // reorderCandidates fills up the candidates array directly + reorderCandidates(signatures, candidates); if (!candidates.length) { error(node, Diagnostics.Supplied_parameters_do_not_match_any_signature_of_call_target); return resolveErrorCall(node); @@ -6282,60 +6327,6 @@ module ts { return undefined; } - // The candidate list orders groups in reverse, but within a group signatures are kept in declaration order - // A nit here is that we reorder only signatures that belong to the same symbol, - // so order how inherited signatures are processed is still preserved. - // interface A { (x: string): void } - // interface B extends A { (x: 'foo'): string } - // var b: B; - // b('foo') // <- here overloads should be processed as [(x:'foo'): string, (x: string): void] - function collectCandidates(): void { - var result = candidates; - var lastParent: Node; - var lastSymbol: Symbol; - var cutoffIndex: number = 0; - var index: number; - var specializedIndex: number = -1; - var spliceIndex: number; - Debug.assert(!result.length); - for (var i = 0; i < signatures.length; i++) { - var signature = signatures[i]; - var symbol = signature.declaration && getSymbolOfNode(signature.declaration); - var parent = signature.declaration && signature.declaration.parent; - if (!lastSymbol || symbol === lastSymbol) { - if (lastParent && parent === lastParent) { - index++; - } - else { - lastParent = parent; - index = cutoffIndex; - } - } - else { - // current declaration belongs to a different symbol - // set cutoffIndex so re-orderings in the future won't change result set from 0 to cutoffIndex - index = cutoffIndex = result.length; - lastParent = parent; - } - lastSymbol = symbol; - - // specialized signatures always need to be placed before non-specialized signatures regardless - // of the cutoff position; see GH#1133 - if (signature.hasStringLiterals) { - specializedIndex++; - spliceIndex = specializedIndex; - // The cutoff index always needs to be greater than or equal to the specialized signature index - // in order to prevent non-specialized signatures from being added before a specialized - // signature. - cutoffIndex++; - } - else { - spliceIndex = index; - } - - result.splice(spliceIndex, 0, signature); - } - } } function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[]): Signature { @@ -6391,6 +6382,13 @@ module ts { } function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[]): Signature { + if (node.arguments && languageVersion < ScriptTarget.ES6) { + var spreadIndex = getSpreadArgumentIndex(node.arguments); + if (spreadIndex >= 0) { + error(node.arguments[spreadIndex], Diagnostics.Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher); + } + } + var expressionType = checkExpression(node.expression); // TS 1.0 spec: 4.11 // If ConstructExpr is of type Any, Args can be any argument @@ -6536,9 +6534,14 @@ module ts { } function getTypeAtPosition(signature: Signature, pos: number): Type { + if (pos >= 0) { + return signature.hasRestParameter ? + pos < signature.parameters.length - 1 ? getTypeOfSymbol(signature.parameters[pos]) : getRestTypeOfSignature(signature) : + pos < signature.parameters.length ? getTypeOfSymbol(signature.parameters[pos]) : anyType; + } return signature.hasRestParameter ? - pos < signature.parameters.length - 1 ? getTypeOfSymbol(signature.parameters[pos]) : getRestTypeOfSignature(signature) : - pos < signature.parameters.length ? getTypeOfSymbol(signature.parameters[pos]) : anyType; + getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]) : + anyArrayType; } function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper) { diff --git a/src/compiler/diagnosticInformationMap.generated.ts b/src/compiler/diagnosticInformationMap.generated.ts index 1188d34619132..a0e323dca1c02 100644 --- a/src/compiler/diagnosticInformationMap.generated.ts +++ b/src/compiler/diagnosticInformationMap.generated.ts @@ -303,6 +303,7 @@ module ts { this_cannot_be_referenced_in_a_computed_property_name: { code: 2465, category: DiagnosticCategory.Error, key: "'this' cannot be referenced in a computed property name." }, super_cannot_be_referenced_in_a_computed_property_name: { code: 2466, category: DiagnosticCategory.Error, key: "'super' cannot be referenced in a computed property name." }, A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type: { code: 2466, category: DiagnosticCategory.Error, key: "A computed property name cannot reference a type parameter from its containing type." }, + Spread_operator_in_new_expressions_is_only_available_when_targeting_ECMAScript_6_and_higher: { code: 2468, category: DiagnosticCategory.Error, key: "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher." }, 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 b1a794716a489..69db958075f99 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1204,6 +1204,10 @@ "category": "Error", "code": 2466 }, + "Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher.": { + "category": "Error", + "code": 2468 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index 5acaf5602734e..ce0851b2b5681 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -1994,7 +1994,7 @@ module ts { break; } // _a .. _h, _j ... _z, _0, _1, ... - name = "_" + (tempCount < 25 ? String.fromCharCode(tempCount + (tempCount < 8 ? 0: 1) + CharacterCodes.a) : tempCount - 25); + name = "_" + (tempCount < 25 ? String.fromCharCode(tempCount + (tempCount < 8 ? 0 : 1) + CharacterCodes.a) : tempCount - 25); tempCount++; } var result = createNode(SyntaxKind.Identifier); @@ -2423,22 +2423,10 @@ module ts { return true; } - function emitArrayLiteral(node: ArrayLiteralExpression) { - var elements = node.elements; - var length = elements.length; - if (length === 0) { - write("[]"); - return; - } - if (languageVersion >= ScriptTarget.ES6) { - write("["); - emitList(elements, 0, elements.length, /*multiLine*/(node.flags & NodeFlags.MultiLine) !== 0, - /*trailingComma*/ elements.hasTrailingComma); - write("]"); - return; - } + function emitListWithSpread(elements: Expression[], multiLine: boolean, trailingComma: boolean) { var pos = 0; var group = 0; + var length = elements.length; while (pos < length) { // Emit using the pattern .concat(, , ...) if (group === 1) { @@ -2459,8 +2447,7 @@ module ts { i++; } write("["); - emitList(elements, pos, i - pos, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0, - /*trailingComma*/ elements.hasTrailingComma); + emitList(elements, pos, i - pos, multiLine, trailingComma && i === length); write("]"); pos = i; } @@ -2471,6 +2458,23 @@ module ts { } } + function emitArrayLiteral(node: ArrayLiteralExpression) { + var elements = node.elements; + if (elements.length === 0) { + write("[]"); + } + else if (languageVersion >= ScriptTarget.ES6) { + write("["); + emitList(elements, 0, elements.length, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0, + /*trailingComma*/ elements.hasTrailingComma); + write("]"); + } + else { + emitListWithSpread(elements, /*multiLine*/ (node.flags & NodeFlags.MultiLine) !== 0, + /*trailingComma*/ elements.hasTrailingComma); + } + } + function emitObjectLiteral(node: ObjectLiteralExpression) { write("{"); var properties = node.properties; @@ -2565,7 +2569,80 @@ module ts { write("]"); } + function hasSpreadElement(elements: Expression[]) { + return forEach(elements, e => e.kind === SyntaxKind.SpreadElementExpression); + } + + function skipParentheses(node: Expression): Expression { + while (node.kind === SyntaxKind.ParenthesizedExpression || node.kind === SyntaxKind.TypeAssertionExpression) { + node = (node).expression; + } + return node; + } + + function emitCallTarget(node: Expression): Expression { + if (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.ThisKeyword || node.kind === SyntaxKind.SuperKeyword) { + emit(node); + return node; + } + var temp = createTempVariable(node); + recordTempDeclaration(temp); + write("("); + emit(temp); + write(" = "); + emit(node); + write(")"); + return temp; + } + + function emitCallWithSpread(node: CallExpression) { + var target: Expression; + var expr = skipParentheses(node.expression); + if (expr.kind === SyntaxKind.PropertyAccessExpression) { + // Target will be emitted as "this" argument + target = emitCallTarget((expr).expression); + write("."); + emit((expr).name); + } + else if (expr.kind === SyntaxKind.ElementAccessExpression) { + // Target will be emitted as "this" argument + target = emitCallTarget((expr).expression); + write("["); + emit((expr).argumentExpression); + write("]"); + } + else if (expr.kind === SyntaxKind.SuperKeyword) { + target = expr; + write("_super"); + } + else { + emit(node.expression); + } + write(".apply("); + if (target) { + if (target.kind === SyntaxKind.SuperKeyword) { + // Calls of form super(...) and super.foo(...) + emitThis(target); + } + else { + // Calls of form obj.foo(...) + emit(target); + } + } + else { + // Calls of form foo(...) + write("void 0"); + } + write(", "); + emitListWithSpread(node.arguments, /*multiLine*/ false, /*trailingComma*/ false); + write(")"); + } + function emitCallExpression(node: CallExpression) { + if (languageVersion < ScriptTarget.ES6 && hasSpreadElement(node.arguments)) { + emitCallWithSpread(node); + return; + } var superCall = false; if (node.expression.kind === SyntaxKind.SuperKeyword) { write("_super"); diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 06db869ad54f4..e8c6bb9c9dbaa 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -1516,7 +1516,6 @@ module ts { case ParsingContext.TypeParameters: return isIdentifier(); case ParsingContext.ArgumentExpressions: - return token === SyntaxKind.CommaToken || isStartOfExpression(); case ParsingContext.ArrayLiteralMembers: return token === SyntaxKind.CommaToken || token === SyntaxKind.DotDotDotToken || isStartOfExpression(); case ParsingContext.Parameters: @@ -3605,12 +3604,6 @@ module ts { return finishNode(node); } - function parseAssignmentExpressionOrOmittedExpression(): Expression { - return token === SyntaxKind.CommaToken - ? createNode(SyntaxKind.OmittedExpression) - : parseAssignmentExpressionOrHigher(); - } - function parseSpreadElement(): Expression { var node = createNode(SyntaxKind.SpreadElementExpression); parseExpected(SyntaxKind.DotDotDotToken); @@ -3618,19 +3611,21 @@ module ts { return finishNode(node); } - function parseArrayLiteralElement(): Expression { - return token === SyntaxKind.DotDotDotToken ? parseSpreadElement() : parseAssignmentExpressionOrOmittedExpression(); + function parseArgumentOrArrayLiteralElement(): Expression { + return token === SyntaxKind.DotDotDotToken ? parseSpreadElement() : + token === SyntaxKind.CommaToken ? createNode(SyntaxKind.OmittedExpression) : + parseAssignmentExpressionOrHigher(); } function parseArgumentExpression(): Expression { - return allowInAnd(parseAssignmentExpressionOrOmittedExpression); + return allowInAnd(parseArgumentOrArrayLiteralElement); } function parseArrayLiteralExpression(): ArrayLiteralExpression { var node = createNode(SyntaxKind.ArrayLiteralExpression); parseExpected(SyntaxKind.OpenBracketToken); if (scanner.hasPrecedingLineBreak()) node.flags |= NodeFlags.MultiLine; - node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArrayLiteralElement); + node.elements = parseDelimitedList(ParsingContext.ArrayLiteralMembers, parseArgumentOrArrayLiteralElement); parseExpected(SyntaxKind.CloseBracketToken); return finishNode(node); } diff --git a/tests/baselines/reference/callWithSpread.errors.txt b/tests/baselines/reference/callWithSpread.errors.txt new file mode 100644 index 0000000000000..7ea026f434662 --- /dev/null +++ b/tests/baselines/reference/callWithSpread.errors.txt @@ -0,0 +1,59 @@ +tests/cases/conformance/expressions/functionCalls/callWithSpread.ts(52,21): error TS2468: Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher. + + +==== tests/cases/conformance/expressions/functionCalls/callWithSpread.ts (1 errors) ==== + interface X { + foo(x: number, y: number, ...z: string[]); + } + + function foo(x: number, y: number, ...z: string[]) { + } + + var a: string[]; + var z: number[]; + var obj: X; + var xa: X[]; + + foo(1, 2, "abc"); + foo(1, 2, ...a); + foo(1, 2, ...a, "abc"); + + obj.foo(1, 2, "abc"); + obj.foo(1, 2, ...a); + obj.foo(1, 2, ...a, "abc"); + + (obj.foo)(1, 2, "abc"); + (obj.foo)(1, 2, ...a); + (obj.foo)(1, 2, ...a, "abc"); + + xa[1].foo(1, 2, "abc"); + xa[1].foo(1, 2, ...a); + xa[1].foo(1, 2, ...a, "abc"); + + (xa[1].foo)(...[1, 2, "abc"]); + + class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } + } + + class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } + } + + // Only supported in when target is ES6 + var c = new C(1, 2, ...a); + ~~~~ +!!! error TS2468: Spread operator in 'new' expressions is only available when targeting ECMAScript 6 and higher. + \ No newline at end of file diff --git a/tests/baselines/reference/callWithSpread.js b/tests/baselines/reference/callWithSpread.js new file mode 100644 index 0000000000000..d676c0f2a3380 --- /dev/null +++ b/tests/baselines/reference/callWithSpread.js @@ -0,0 +1,117 @@ +//// [callWithSpread.ts] +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} + +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); + + +//// [callWithSpread.js] +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +function foo(x, y) { + var z = []; + for (var _i = 2; _i < arguments.length; _i++) { + z[_i - 2] = arguments[_i]; + } +} +var a; +var z; +var obj; +var xa; +foo(1, 2, "abc"); +foo.apply(void 0, [1, 2].concat(a)); +foo.apply(void 0, [1, 2].concat(a, ["abc"])); +obj.foo(1, 2, "abc"); +obj.foo.apply(obj, [1, 2].concat(a)); +obj.foo.apply(obj, [1, 2].concat(a, ["abc"])); +(obj.foo)(1, 2, "abc"); +obj.foo.apply(obj, [1, 2].concat(a)); +obj.foo.apply(obj, [1, 2].concat(a, ["abc"])); +xa[1].foo(1, 2, "abc"); +(_a = xa[1]).foo.apply(_a, [1, 2].concat(a)); +(_b = xa[1]).foo.apply(_b, [1, 2].concat(a, ["abc"])); +(_c = xa[1]).foo.apply(_c, [1, 2, "abc"]); +var C = (function () { + function C(x, y) { + var z = []; + for (var _i = 2; _i < arguments.length; _i++) { + z[_i - 2] = arguments[_i]; + } + this.foo(x, y); + this.foo.apply(this, [x, y].concat(z)); + } + C.prototype.foo = function (x, y) { + var z = []; + for (var _i = 2; _i < arguments.length; _i++) { + z[_i - 2] = arguments[_i]; + } + }; + return C; +})(); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.call(this, 1, 2); + _super.apply(this, [1, 2].concat(a)); + } + D.prototype.foo = function () { + _super.prototype.foo.call(this, 1, 2); + _super.prototype.foo.apply(this, [1, 2].concat(a)); + }; + return D; +})(C); +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); +var _a, _b, _c; diff --git a/tests/baselines/reference/callWithSpreadES6.js b/tests/baselines/reference/callWithSpreadES6.js new file mode 100644 index 0000000000000..bdb5aab33ec0c --- /dev/null +++ b/tests/baselines/reference/callWithSpreadES6.js @@ -0,0 +1,105 @@ +//// [callWithSpreadES6.ts] + +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} + +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); + + +//// [callWithSpreadES6.js] +var __extends = this.__extends || function (d, b) { + for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; + function __() { this.constructor = d; } + __.prototype = b.prototype; + d.prototype = new __(); +}; +function foo(x, y, ...z) { +} +var a; +var z; +var obj; +var xa; +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); +xa[1].foo(...[1, 2, "abc"]); +var C = (function () { + function C(x, y, ...z) { + this.foo(x, y); + this.foo(x, y, ...z); + } + C.prototype.foo = function (x, y, ...z) { + }; + return C; +})(); +var D = (function (_super) { + __extends(D, _super); + function D() { + _super.call(this, 1, 2); + _super.call(this, 1, 2, ...a); + } + D.prototype.foo = function () { + _super.prototype.foo.call(this, 1, 2); + _super.prototype.foo.call(this, 1, 2, ...a); + }; + return D; +})(C); +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); diff --git a/tests/baselines/reference/callWithSpreadES6.types b/tests/baselines/reference/callWithSpreadES6.types new file mode 100644 index 0000000000000..8a75d21e4efba --- /dev/null +++ b/tests/baselines/reference/callWithSpreadES6.types @@ -0,0 +1,196 @@ +=== tests/cases/conformance/expressions/functionCalls/callWithSpreadES6.ts === + +interface X { +>X : X + + foo(x: number, y: number, ...z: string[]); +>foo : (x: number, y: number, ...z: string[]) => any +>x : number +>y : number +>z : string[] +} + +function foo(x: number, y: number, ...z: string[]) { +>foo : (x: number, y: number, ...z: string[]) => void +>x : number +>y : number +>z : string[] +} + +var a: string[]; +>a : string[] + +var z: number[]; +>z : number[] + +var obj: X; +>obj : X +>X : X + +var xa: X[]; +>xa : X[] +>X : X + +foo(1, 2, "abc"); +>foo(1, 2, "abc") : void +>foo : (x: number, y: number, ...z: string[]) => void + +foo(1, 2, ...a); +>foo(1, 2, ...a) : void +>foo : (x: number, y: number, ...z: string[]) => void +>a : string[] + +foo(1, 2, ...a, "abc"); +>foo(1, 2, ...a, "abc") : void +>foo : (x: number, y: number, ...z: string[]) => void +>a : string[] + +obj.foo(1, 2, "abc"); +>obj.foo(1, 2, "abc") : any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any + +obj.foo(1, 2, ...a); +>obj.foo(1, 2, ...a) : any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +obj.foo(1, 2, ...a, "abc"); +>obj.foo(1, 2, ...a, "abc") : any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +(obj.foo)(1, 2, "abc"); +>(obj.foo)(1, 2, "abc") : any +>(obj.foo) : (x: number, y: number, ...z: string[]) => any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any + +(obj.foo)(1, 2, ...a); +>(obj.foo)(1, 2, ...a) : any +>(obj.foo) : (x: number, y: number, ...z: string[]) => any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +(obj.foo)(1, 2, ...a, "abc"); +>(obj.foo)(1, 2, ...a, "abc") : any +>(obj.foo) : (x: number, y: number, ...z: string[]) => any +>obj.foo : (x: number, y: number, ...z: string[]) => any +>obj : X +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +xa[1].foo(1, 2, "abc"); +>xa[1].foo(1, 2, "abc") : any +>xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1] : X +>xa : X[] +>foo : (x: number, y: number, ...z: string[]) => any + +xa[1].foo(1, 2, ...a); +>xa[1].foo(1, 2, ...a) : any +>xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1] : X +>xa : X[] +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +xa[1].foo(1, 2, ...a, "abc"); +>xa[1].foo(1, 2, ...a, "abc") : any +>xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1] : X +>xa : X[] +>foo : (x: number, y: number, ...z: string[]) => any +>a : string[] + +(xa[1].foo)(...[1, 2, "abc"]); +>(xa[1].foo)(...[1, 2, "abc"]) : any +>(xa[1].foo) : Function +>xa[1].foo : Function +>Function : Function +>xa[1].foo : (x: number, y: number, ...z: string[]) => any +>xa[1] : X +>xa : X[] +>foo : (x: number, y: number, ...z: string[]) => any +>[1, 2, "abc"] : (string | number)[] + +class C { +>C : C + + constructor(x: number, y: number, ...z: string[]) { +>x : number +>y : number +>z : string[] + + this.foo(x, y); +>this.foo(x, y) : void +>this.foo : (x: number, y: number, ...z: string[]) => void +>this : C +>foo : (x: number, y: number, ...z: string[]) => void +>x : number +>y : number + + this.foo(x, y, ...z); +>this.foo(x, y, ...z) : void +>this.foo : (x: number, y: number, ...z: string[]) => void +>this : C +>foo : (x: number, y: number, ...z: string[]) => void +>x : number +>y : number +>z : string[] + } + foo(x: number, y: number, ...z: string[]) { +>foo : (x: number, y: number, ...z: string[]) => void +>x : number +>y : number +>z : string[] + } +} + +class D extends C { +>D : D +>C : C + + constructor() { + super(1, 2); +>super(1, 2) : void +>super : typeof C + + super(1, 2, ...a); +>super(1, 2, ...a) : void +>super : typeof C +>a : string[] + } + foo() { +>foo : () => void + + super.foo(1, 2); +>super.foo(1, 2) : void +>super.foo : (x: number, y: number, ...z: string[]) => void +>super : C +>foo : (x: number, y: number, ...z: string[]) => void + + super.foo(1, 2, ...a); +>super.foo(1, 2, ...a) : void +>super.foo : (x: number, y: number, ...z: string[]) => void +>super : C +>foo : (x: number, y: number, ...z: string[]) => void +>a : string[] + } +} + +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); +>c : C +>new C(1, 2, ...a) : C +>C : typeof C +>a : string[] + diff --git a/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts b/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts new file mode 100644 index 0000000000000..9acba00697a20 --- /dev/null +++ b/tests/cases/conformance/expressions/functionCalls/callWithSpread.ts @@ -0,0 +1,52 @@ +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} + +// Only supported in when target is ES6 +var c = new C(1, 2, ...a); diff --git a/tests/cases/conformance/expressions/functionCalls/callWithSpreadES6.ts b/tests/cases/conformance/expressions/functionCalls/callWithSpreadES6.ts new file mode 100644 index 0000000000000..2f7d16ba368a0 --- /dev/null +++ b/tests/cases/conformance/expressions/functionCalls/callWithSpreadES6.ts @@ -0,0 +1,54 @@ +// @target: ES6 + +interface X { + foo(x: number, y: number, ...z: string[]); +} + +function foo(x: number, y: number, ...z: string[]) { +} + +var a: string[]; +var z: number[]; +var obj: X; +var xa: X[]; + +foo(1, 2, "abc"); +foo(1, 2, ...a); +foo(1, 2, ...a, "abc"); + +obj.foo(1, 2, "abc"); +obj.foo(1, 2, ...a); +obj.foo(1, 2, ...a, "abc"); + +(obj.foo)(1, 2, "abc"); +(obj.foo)(1, 2, ...a); +(obj.foo)(1, 2, ...a, "abc"); + +xa[1].foo(1, 2, "abc"); +xa[1].foo(1, 2, ...a); +xa[1].foo(1, 2, ...a, "abc"); + +(xa[1].foo)(...[1, 2, "abc"]); + +class C { + constructor(x: number, y: number, ...z: string[]) { + this.foo(x, y); + this.foo(x, y, ...z); + } + foo(x: number, y: number, ...z: string[]) { + } +} + +class D extends C { + constructor() { + super(1, 2); + super(1, 2, ...a); + } + foo() { + super.foo(1, 2); + super.foo(1, 2, ...a); + } +} + +// Only supported in when target is ES6 +var c = new C(1, 2, ...a);