Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the error when use spread arguments twice #33069

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 18 additions & 5 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(<SpreadElement>args[firstSpreadArgIndex]);
for (let i = firstSpreadArgIndex; i < args.length; i++) {
totalCount += isSpreadArgument(args[i]) ? countSpreadArgumentLength(<SpreadElement>args[i]) : 1;
}
return totalCount >= getMinArgumentCount(signature) && (hasEffectiveRestParameter(signature) || totalCount < getParameterCount(signature));
}
}

Expand All @@ -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<TypeNode> | 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.
Expand Down Expand Up @@ -24840,7 +24853,7 @@ namespace ts {
const spreadArgument = <SpreadElement>args[length - 1];
const type = flowLoopCount ? checkExpression(spreadArgument.expression) : checkExpressionCached(spreadArgument.expression);
if (isTupleType(type)) {
const typeArguments = getTypeArguments(<TypeReference>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);
Expand Down
25 changes: 18 additions & 7 deletions tests/baselines/reference/callWithSpread3.errors.txt
Original file line number Diff line number Diff line change
@@ -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.
Expand All @@ -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.
!!! error TS2554: Expected 2 arguments, but got 3.

// ok
takeTwoOrMore(...t4);
takeTwoOrMore(...t4, ...t5);
takeTwoOrMore(...t4, ...t4);
takeTwoOrMore(...t5, ...t4);

19 changes: 18 additions & 1 deletion tests/baselines/reference/callWithSpread3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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));
33 changes: 33 additions & 0 deletions tests/baselines/reference/callWithSpread3.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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))

44 changes: 44 additions & 0 deletions tests/baselines/reference/callWithSpread3.types
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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[]]

Original file line number Diff line number Diff line change
@@ -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
takeTwo(...t3); // error on ...t3

// ok
takeTwoOrMore(...t4);
takeTwoOrMore(...t4, ...t5);
takeTwoOrMore(...t4, ...t4);
takeTwoOrMore(...t5, ...t4);