diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4a896d2a70fc2..421d910bba026 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24364,10 +24364,18 @@ namespace ts { // If we are missing the close parenthesis, the call is incomplete. callIsIncomplete = node.arguments.end === node.end; - // If a spread argument is present, check that it corresponds to a rest parameter or at least that it's in the valid range. - const spreadArgIndex = getSpreadArgumentIndex(args); - if (spreadArgIndex >= 0) { - return spreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || spreadArgIndex < getParameterCount(signature)); + // If one or more spread arguments are present, check that they correspond to a rest parameter or at least that they are in the valid range. + const firstSpreadArgIndex = getSpreadArgumentIndex(args); + if (firstSpreadArgIndex >= 0) { + if (firstSpreadArgIndex === args.length - 1) { + return firstSpreadArgIndex >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || firstSpreadArgIndex < getParameterCount(signature)); + } + + let totalCount = countSpreadArgumentLength(args[firstSpreadArgIndex]); + for (let i = firstSpreadArgIndex; i < args.length; i++) { + totalCount += isSpreadArgument(args[i]) ? countSpreadArgumentLength(args[i]) : 1; + } + return totalCount >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || totalCount < getParameterCount(signature)); } } @@ -24390,6 +24398,11 @@ namespace ts { return true; } + function countSpreadArgumentLength(argment: SpreadElement): number { + const type = flowLoopCount ? checkExpression(argment.expression) : checkExpressionCached(argment.expression); + return isTupleType(type) ? getTypeArguments(type).length : 1; + } + function hasCorrectTypeArgumentArity(signature: Signature, typeArguments: NodeArray | undefined) { // 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. @@ -24840,7 +24853,7 @@ namespace ts { const spreadArgument = args[length - 1]; const type = flowLoopCount ? checkExpression(spreadArgument.expression) : checkExpressionCached(spreadArgument.expression); if (isTupleType(type)) { - const typeArguments = getTypeArguments(type); + const typeArguments = getTypeArguments(type); const restIndex = type.target.hasRestElement ? typeArguments.length - 1 : -1; const syntheticArgs = map(typeArguments, (t, i) => createSyntheticExpression(spreadArgument, t, /*isSpread*/ i === restIndex)); return concatenate(args.slice(0, length - 1), syntheticArgs); diff --git a/tests/baselines/reference/callWithSpread3.errors.txt b/tests/baselines/reference/callWithSpread3.errors.txt index 6e45b5ea7979f..e5f722efe6aa6 100644 --- a/tests/baselines/reference/callWithSpread3.errors.txt +++ b/tests/baselines/reference/callWithSpread3.errors.txt @@ -1,16 +1,20 @@ -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(5,14): error TS2554: Expected 2 arguments, but got 3. -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(6,19): error TS2554: Expected 2 arguments, but got 5. -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(7,19): error TS2556: Expected 2 arguments, but got 4 or more. -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(8,19): error TS2556: Expected 2 arguments, but got 5 or more. -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(9,16): error TS2556: Expected 2 arguments, but got 1 or more. -tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(10,9): error TS2554: Expected 2 arguments, but got 3. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(9,14): error TS2554: Expected 2 arguments, but got 3. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(10,19): error TS2554: Expected 2 arguments, but got 5. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(11,19): error TS2556: Expected 2 arguments, but got 4 or more. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(12,19): error TS2556: Expected 2 arguments, but got 5 or more. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(13,16): error TS2556: Expected 2 arguments, but got 1 or more. +tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(14,9): error TS2554: Expected 2 arguments, but got 3. ==== tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts (6 errors) ==== declare function takeTwo(a: string, b: string): void; declare const t2: [string, string]; declare const t3: [string, string, string]; + declare function takeTwoOrMore (a: string, b: string, ...c: string[]): void + declare const t4: [string, string, ...string[]] + declare const t5: string[] + // error takeTwo('a', ...t2); // error on ...t2 ~~~~~ !!! error TS2554: Expected 2 arguments, but got 3. @@ -29,4 +33,11 @@ tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts(10,9): erro !!! related TS6210 tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts:1:37: An argument for 'b' was not provided. takeTwo(...t3); // error on ...t3 ~~~~~ -!!! error TS2554: Expected 2 arguments, but got 3. \ No newline at end of file +!!! error TS2554: Expected 2 arguments, but got 3. + + // ok + takeTwoOrMore(...t4); + takeTwoOrMore(...t4, ...t5); + takeTwoOrMore(...t4, ...t4); + takeTwoOrMore(...t5, ...t4); + \ No newline at end of file diff --git a/tests/baselines/reference/callWithSpread3.js b/tests/baselines/reference/callWithSpread3.js index bcb30625f46ce..040dd6ceed880 100644 --- a/tests/baselines/reference/callWithSpread3.js +++ b/tests/baselines/reference/callWithSpread3.js @@ -2,13 +2,24 @@ declare function takeTwo(a: string, b: string): void; declare const t2: [string, string]; declare const t3: [string, string, string]; +declare function takeTwoOrMore (a: string, b: string, ...c: string[]): void +declare const t4: [string, string, ...string[]] +declare const t5: string[] +// error takeTwo('a', ...t2); // error on ...t2 takeTwo('a', 'b', 'c', ...t2); // error on 'c' and ...t2 takeTwo('a', 'b', ...t2, 'c'); // error on ...t2 and 'c' takeTwo('a', 'b', 'c', ...t2, 'd'); // error on 'c', ...t2 and 'd' takeTwo(...t2, 'a'); // error on 'a' -takeTwo(...t3); // error on ...t3 +takeTwo(...t3); // error on ...t3 + +// ok +takeTwoOrMore(...t4); +takeTwoOrMore(...t4, ...t5); +takeTwoOrMore(...t4, ...t4); +takeTwoOrMore(...t5, ...t4); + //// [callWithSpread3.js] var __spreadArrays = (this && this.__spreadArrays) || function () { @@ -18,9 +29,15 @@ var __spreadArrays = (this && this.__spreadArrays) || function () { r[k] = a[j]; return r; }; +// error takeTwo.apply(void 0, __spreadArrays(['a'], t2)); // error on ...t2 takeTwo.apply(void 0, __spreadArrays(['a', 'b', 'c'], t2)); // error on 'c' and ...t2 takeTwo.apply(void 0, __spreadArrays(['a', 'b'], t2, ['c'])); // error on ...t2 and 'c' takeTwo.apply(void 0, __spreadArrays(['a', 'b', 'c'], t2, ['d'])); // error on 'c', ...t2 and 'd' takeTwo.apply(void 0, __spreadArrays(t2, ['a'])); // error on 'a' takeTwo.apply(void 0, t3); // error on ...t3 +// ok +takeTwoOrMore.apply(void 0, t4); +takeTwoOrMore.apply(void 0, __spreadArrays(t4, t5)); +takeTwoOrMore.apply(void 0, __spreadArrays(t4, t4)); +takeTwoOrMore.apply(void 0, __spreadArrays(t5, t4)); diff --git a/tests/baselines/reference/callWithSpread3.symbols b/tests/baselines/reference/callWithSpread3.symbols index 3f0ea2d8dd814..b8a90f181d765 100644 --- a/tests/baselines/reference/callWithSpread3.symbols +++ b/tests/baselines/reference/callWithSpread3.symbols @@ -10,6 +10,19 @@ declare const t2: [string, string]; declare const t3: [string, string, string]; >t3 : Symbol(t3, Decl(callWithSpread3.ts, 2, 13)) +declare function takeTwoOrMore (a: string, b: string, ...c: string[]): void +>takeTwoOrMore : Symbol(takeTwoOrMore, Decl(callWithSpread3.ts, 2, 43)) +>a : Symbol(a, Decl(callWithSpread3.ts, 3, 32)) +>b : Symbol(b, Decl(callWithSpread3.ts, 3, 42)) +>c : Symbol(c, Decl(callWithSpread3.ts, 3, 53)) + +declare const t4: [string, string, ...string[]] +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) + +declare const t5: string[] +>t5 : Symbol(t5, Decl(callWithSpread3.ts, 5, 13)) + +// error takeTwo('a', ...t2); // error on ...t2 >takeTwo : Symbol(takeTwo, Decl(callWithSpread3.ts, 0, 0)) >t2 : Symbol(t2, Decl(callWithSpread3.ts, 1, 13)) @@ -34,3 +47,23 @@ takeTwo(...t3); // error on ...t3 >takeTwo : Symbol(takeTwo, Decl(callWithSpread3.ts, 0, 0)) >t3 : Symbol(t3, Decl(callWithSpread3.ts, 2, 13)) +// ok +takeTwoOrMore(...t4); +>takeTwoOrMore : Symbol(takeTwoOrMore, Decl(callWithSpread3.ts, 2, 43)) +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) + +takeTwoOrMore(...t4, ...t5); +>takeTwoOrMore : Symbol(takeTwoOrMore, Decl(callWithSpread3.ts, 2, 43)) +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) +>t5 : Symbol(t5, Decl(callWithSpread3.ts, 5, 13)) + +takeTwoOrMore(...t4, ...t4); +>takeTwoOrMore : Symbol(takeTwoOrMore, Decl(callWithSpread3.ts, 2, 43)) +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) + +takeTwoOrMore(...t5, ...t4); +>takeTwoOrMore : Symbol(takeTwoOrMore, Decl(callWithSpread3.ts, 2, 43)) +>t5 : Symbol(t5, Decl(callWithSpread3.ts, 5, 13)) +>t4 : Symbol(t4, Decl(callWithSpread3.ts, 4, 13)) + diff --git a/tests/baselines/reference/callWithSpread3.types b/tests/baselines/reference/callWithSpread3.types index 5bbb0037774e8..8d6f8c1f38896 100644 --- a/tests/baselines/reference/callWithSpread3.types +++ b/tests/baselines/reference/callWithSpread3.types @@ -10,6 +10,19 @@ declare const t2: [string, string]; declare const t3: [string, string, string]; >t3 : [string, string, string] +declare function takeTwoOrMore (a: string, b: string, ...c: string[]): void +>takeTwoOrMore : (a: string, b: string, ...c: string[]) => void +>a : string +>b : string +>c : string[] + +declare const t4: [string, string, ...string[]] +>t4 : [string, string, ...string[]] + +declare const t5: string[] +>t5 : string[] + +// error takeTwo('a', ...t2); // error on ...t2 >takeTwo('a', ...t2) : void >takeTwo : (a: string, b: string) => void @@ -58,3 +71,34 @@ takeTwo(...t3); // error on ...t3 >...t3 : string >t3 : [string, string, string] +// ok +takeTwoOrMore(...t4); +>takeTwoOrMore(...t4) : void +>takeTwoOrMore : (a: string, b: string, ...c: string[]) => void +>...t4 : string +>t4 : [string, string, ...string[]] + +takeTwoOrMore(...t4, ...t5); +>takeTwoOrMore(...t4, ...t5) : void +>takeTwoOrMore : (a: string, b: string, ...c: string[]) => void +>...t4 : string +>t4 : [string, string, ...string[]] +>...t5 : string +>t5 : string[] + +takeTwoOrMore(...t4, ...t4); +>takeTwoOrMore(...t4, ...t4) : void +>takeTwoOrMore : (a: string, b: string, ...c: string[]) => void +>...t4 : string +>t4 : [string, string, ...string[]] +>...t4 : string +>t4 : [string, string, ...string[]] + +takeTwoOrMore(...t5, ...t4); +>takeTwoOrMore(...t5, ...t4) : void +>takeTwoOrMore : (a: string, b: string, ...c: string[]) => void +>...t5 : string +>t5 : string[] +>...t4 : string +>t4 : [string, string, ...string[]] + diff --git a/tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts b/tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts index bc5d8c2246fa8..bb9ae18382e10 100644 --- a/tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts +++ b/tests/cases/conformance/expressions/functionCalls/callWithSpread3.ts @@ -1,10 +1,20 @@ declare function takeTwo(a: string, b: string): void; declare const t2: [string, string]; declare const t3: [string, string, string]; +declare function takeTwoOrMore (a: string, b: string, ...c: string[]): void +declare const t4: [string, string, ...string[]] +declare const t5: string[] +// error takeTwo('a', ...t2); // error on ...t2 takeTwo('a', 'b', 'c', ...t2); // error on 'c' and ...t2 takeTwo('a', 'b', ...t2, 'c'); // error on ...t2 and 'c' takeTwo('a', 'b', 'c', ...t2, 'd'); // error on 'c', ...t2 and 'd' takeTwo(...t2, 'a'); // error on 'a' -takeTwo(...t3); // error on ...t3 \ No newline at end of file +takeTwo(...t3); // error on ...t3 + +// ok +takeTwoOrMore(...t4); +takeTwoOrMore(...t4, ...t5); +takeTwoOrMore(...t4, ...t4); +takeTwoOrMore(...t5, ...t4);