diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 91ff98be94646..d8be9b4ab3000 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -32200,19 +32200,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSpreadArgumentType(args: readonly Expression[], index: number, argCount: number, restType: Type, context: InferenceContext | undefined, checkMode: CheckMode) { + const inConstContext = isConstTypeVariable(restType); + if (index >= argCount - 1) { const arg = args[argCount - 1]; if (isSpreadArgument(arg)) { // We are inferring from a spread expression in the last argument position, i.e. both the parameter // and the argument are ...x forms. - return getMutableArrayOrTupleType(arg.kind === SyntaxKind.SyntheticExpression ? (arg as SyntheticExpression).type : - checkExpressionWithContextualType((arg as SpreadElement).expression, restType, context, checkMode)); + const spreadType = arg.kind === SyntaxKind.SyntheticExpression ? (arg as SyntheticExpression).type : + checkExpressionWithContextualType((arg as SpreadElement).expression, restType, context, checkMode); + + if (isArrayLikeType(spreadType)) { + return getMutableArrayOrTupleType(spreadType); + } + + return createArrayType(checkIteratedTypeOrElementType(IterationUse.Spread, spreadType, undefinedType, arg.kind === SyntaxKind.SpreadElement ? (arg as SpreadElement).expression : arg), inConstContext); } } const types = []; const flags = []; const names = []; - const inConstContext = isConstTypeVariable(restType); for (let i = index; i < argCount; i++) { const arg = args[i]; if (isSpreadArgument(arg)) { diff --git a/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).errors.txt b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).errors.txt new file mode 100644 index 0000000000000..b96c642c819ef --- /dev/null +++ b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).errors.txt @@ -0,0 +1,37 @@ +tests/cases/compiler/argumentsSpreadRestIterables.tsx(1,22): error TS2304: Cannot find name 'Iterable'. +tests/cases/compiler/argumentsSpreadRestIterables.tsx(8,21): error TS2461: Type '"hello"' is not an array type. +tests/cases/compiler/argumentsSpreadRestIterables.tsx(10,27): error TS2461: Type '"hello"' is not an array type. +tests/cases/compiler/argumentsSpreadRestIterables.tsx(15,19): error TS2461: Type '"hello"' is not an array type. +tests/cases/compiler/argumentsSpreadRestIterables.tsx(17,25): error TS2461: Type '"hello"' is not an array type. + + +==== tests/cases/compiler/argumentsSpreadRestIterables.tsx (5 errors) ==== + declare const itNum: Iterable + ~~~~~~~~ +!!! error TS2304: Cannot find name 'Iterable'. + + ;(function(...rest) {})(...itNum) + ;(function(a, ...rest) {})('', ...itNum) + ;(function(a, ...rest) {})('', true, ...itNum) + + declare function fn1(...args: T): T; + const res1 = fn1(..."hello"); + ~~~~~~~ +!!! error TS2461: Type '"hello"' is not an array type. + const res2 = fn1(...itNum); + const res3 = fn1(true, ..."hello"); + ~~~~~~~ +!!! error TS2461: Type '"hello"' is not an array type. + const res4 = fn1(true, ...itNum); + + // repro from #52781 + declare function foo(...args: T): T; + const p1 = foo(..."hello"); + ~~~~~~~ +!!! error TS2461: Type '"hello"' is not an array type. + const p2 = foo(...itNum); + const p3 = foo(true, ..."hello"); + ~~~~~~~ +!!! error TS2461: Type '"hello"' is not an array type. + const p4 = foo(true, ...itNum); + \ No newline at end of file diff --git a/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).symbols b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).symbols new file mode 100644 index 0000000000000..6724774a315e7 --- /dev/null +++ b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).symbols @@ -0,0 +1,70 @@ +=== tests/cases/compiler/argumentsSpreadRestIterables.tsx === +declare const itNum: Iterable +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) +>Iterable : Symbol(Iterable) + +;(function(...rest) {})(...itNum) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 2, 11)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +;(function(a, ...rest) {})('', ...itNum) +>a : Symbol(a, Decl(argumentsSpreadRestIterables.tsx, 3, 11)) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 3, 13)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +;(function(a, ...rest) {})('', true, ...itNum) +>a : Symbol(a, Decl(argumentsSpreadRestIterables.tsx, 4, 11)) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 4, 13)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +declare function fn1(...args: T): T; +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) +>args : Symbol(args, Decl(argumentsSpreadRestIterables.tsx, 6, 57)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) + +const res1 = fn1(..."hello"); +>res1 : Symbol(res1, Decl(argumentsSpreadRestIterables.tsx, 7, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) + +const res2 = fn1(...itNum); +>res2 : Symbol(res2, Decl(argumentsSpreadRestIterables.tsx, 8, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +const res3 = fn1(true, ..."hello"); +>res3 : Symbol(res3, Decl(argumentsSpreadRestIterables.tsx, 9, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) + +const res4 = fn1(true, ...itNum); +>res4 : Symbol(res4, Decl(argumentsSpreadRestIterables.tsx, 10, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +// repro from #52781 +declare function foo(...args: T): T; +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) +>args : Symbol(args, Decl(argumentsSpreadRestIterables.tsx, 13, 42)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) + +const p1 = foo(..."hello"); +>p1 : Symbol(p1, Decl(argumentsSpreadRestIterables.tsx, 14, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) + +const p2 = foo(...itNum); +>p2 : Symbol(p2, Decl(argumentsSpreadRestIterables.tsx, 15, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +const p3 = foo(true, ..."hello"); +>p3 : Symbol(p3, Decl(argumentsSpreadRestIterables.tsx, 16, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) + +const p4 = foo(true, ...itNum); +>p4 : Symbol(p4, Decl(argumentsSpreadRestIterables.tsx, 17, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + diff --git a/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).types b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).types new file mode 100644 index 0000000000000..401fd666b9040 --- /dev/null +++ b/tests/baselines/reference/argumentsSpreadRestIterables(target=es5).types @@ -0,0 +1,102 @@ +=== tests/cases/compiler/argumentsSpreadRestIterables.tsx === +declare const itNum: Iterable +>itNum : Iterable + +;(function(...rest) {})(...itNum) +>(function(...rest) {})(...itNum) : void +>(function(...rest) {}) : (...rest: Iterable) => void +>function(...rest) {} : (...rest: Iterable) => void +>rest : Iterable +>...itNum : Iterable +>itNum : Iterable + +;(function(a, ...rest) {})('', ...itNum) +>(function(a, ...rest) {})('', ...itNum) : void +>(function(a, ...rest) {}) : (a: string, ...rest: Iterable) => void +>function(a, ...rest) {} : (a: string, ...rest: Iterable) => void +>a : string +>rest : Iterable +>'' : "" +>...itNum : Iterable +>itNum : Iterable + +;(function(a, ...rest) {})('', true, ...itNum) +>(function(a, ...rest) {})('', true, ...itNum) : void +>(function(a, ...rest) {}) : (a: string, rest_0: boolean, ...rest_1: any[]) => void +>function(a, ...rest) {} : (a: string, rest_0: boolean, ...rest_1: any[]) => void +>a : string +>rest : [boolean, ...any[]] +>'' : "" +>true : true +>...itNum : Iterable +>itNum : Iterable + +declare function fn1(...args: T): T; +>fn1 : (...args: T) => T +>args : T + +const res1 = fn1(..."hello"); +>res1 : readonly any[] +>fn1(..."hello") : readonly any[] +>fn1 : (...args: T) => T +>..."hello" : any +>"hello" : "hello" + +const res2 = fn1(...itNum); +>res2 : Iterable +>fn1(...itNum) : Iterable +>fn1 : (...args: T) => T +>...itNum : Iterable +>itNum : Iterable + +const res3 = fn1(true, ..."hello"); +>res3 : readonly [true, ...any[]] +>fn1(true, ..."hello") : readonly [true, ...any[]] +>fn1 : (...args: T) => T +>true : true +>..."hello" : any +>"hello" : "hello" + +const res4 = fn1(true, ...itNum); +>res4 : readonly [true, ...any[]] +>fn1(true, ...itNum) : readonly [true, ...any[]] +>fn1 : (...args: T) => T +>true : true +>...itNum : Iterable +>itNum : Iterable + +// repro from #52781 +declare function foo(...args: T): T; +>foo : (...args: T) => T +>args : T + +const p1 = foo(..."hello"); +>p1 : any[] +>foo(..."hello") : any[] +>foo : (...args: T) => T +>..."hello" : any +>"hello" : "hello" + +const p2 = foo(...itNum); +>p2 : Iterable +>foo(...itNum) : Iterable +>foo : (...args: T) => T +>...itNum : Iterable +>itNum : Iterable + +const p3 = foo(true, ..."hello"); +>p3 : [boolean, ...any[]] +>foo(true, ..."hello") : [boolean, ...any[]] +>foo : (...args: T) => T +>true : true +>..."hello" : any +>"hello" : "hello" + +const p4 = foo(true, ...itNum); +>p4 : [boolean, ...any[]] +>foo(true, ...itNum) : [boolean, ...any[]] +>foo : (...args: T) => T +>true : true +>...itNum : Iterable +>itNum : Iterable + diff --git a/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).symbols b/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).symbols new file mode 100644 index 0000000000000..b37a980224fde --- /dev/null +++ b/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).symbols @@ -0,0 +1,70 @@ +=== tests/cases/compiler/argumentsSpreadRestIterables.tsx === +declare const itNum: Iterable +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) +>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --)) + +;(function(...rest) {})(...itNum) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 2, 11)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +;(function(a, ...rest) {})('', ...itNum) +>a : Symbol(a, Decl(argumentsSpreadRestIterables.tsx, 3, 11)) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 3, 13)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +;(function(a, ...rest) {})('', true, ...itNum) +>a : Symbol(a, Decl(argumentsSpreadRestIterables.tsx, 4, 11)) +>rest : Symbol(rest, Decl(argumentsSpreadRestIterables.tsx, 4, 13)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +declare function fn1(...args: T): T; +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) +>args : Symbol(args, Decl(argumentsSpreadRestIterables.tsx, 6, 57)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 6, 21)) + +const res1 = fn1(..."hello"); +>res1 : Symbol(res1, Decl(argumentsSpreadRestIterables.tsx, 7, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) + +const res2 = fn1(...itNum); +>res2 : Symbol(res2, Decl(argumentsSpreadRestIterables.tsx, 8, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +const res3 = fn1(true, ..."hello"); +>res3 : Symbol(res3, Decl(argumentsSpreadRestIterables.tsx, 9, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) + +const res4 = fn1(true, ...itNum); +>res4 : Symbol(res4, Decl(argumentsSpreadRestIterables.tsx, 10, 5)) +>fn1 : Symbol(fn1, Decl(argumentsSpreadRestIterables.tsx, 4, 46)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +// repro from #52781 +declare function foo(...args: T): T; +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) +>args : Symbol(args, Decl(argumentsSpreadRestIterables.tsx, 13, 42)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) +>T : Symbol(T, Decl(argumentsSpreadRestIterables.tsx, 13, 21)) + +const p1 = foo(..."hello"); +>p1 : Symbol(p1, Decl(argumentsSpreadRestIterables.tsx, 14, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) + +const p2 = foo(...itNum); +>p2 : Symbol(p2, Decl(argumentsSpreadRestIterables.tsx, 15, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + +const p3 = foo(true, ..."hello"); +>p3 : Symbol(p3, Decl(argumentsSpreadRestIterables.tsx, 16, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) + +const p4 = foo(true, ...itNum); +>p4 : Symbol(p4, Decl(argumentsSpreadRestIterables.tsx, 17, 5)) +>foo : Symbol(foo, Decl(argumentsSpreadRestIterables.tsx, 10, 33)) +>itNum : Symbol(itNum, Decl(argumentsSpreadRestIterables.tsx, 0, 13)) + diff --git a/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).types b/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).types new file mode 100644 index 0000000000000..a1543ea0d3054 --- /dev/null +++ b/tests/baselines/reference/argumentsSpreadRestIterables(target=esnext).types @@ -0,0 +1,102 @@ +=== tests/cases/compiler/argumentsSpreadRestIterables.tsx === +declare const itNum: Iterable +>itNum : Iterable + +;(function(...rest) {})(...itNum) +>(function(...rest) {})(...itNum) : void +>(function(...rest) {}) : (...rest: number[]) => void +>function(...rest) {} : (...rest: number[]) => void +>rest : number[] +>...itNum : number +>itNum : Iterable + +;(function(a, ...rest) {})('', ...itNum) +>(function(a, ...rest) {})('', ...itNum) : void +>(function(a, ...rest) {}) : (a: string, ...rest: number[]) => void +>function(a, ...rest) {} : (a: string, ...rest: number[]) => void +>a : string +>rest : number[] +>'' : "" +>...itNum : number +>itNum : Iterable + +;(function(a, ...rest) {})('', true, ...itNum) +>(function(a, ...rest) {})('', true, ...itNum) : void +>(function(a, ...rest) {}) : (a: string, rest_0: boolean, ...rest_1: number[]) => void +>function(a, ...rest) {} : (a: string, rest_0: boolean, ...rest_1: number[]) => void +>a : string +>rest : [boolean, ...number[]] +>'' : "" +>true : true +>...itNum : number +>itNum : Iterable + +declare function fn1(...args: T): T; +>fn1 : (...args: T) => T +>args : T + +const res1 = fn1(..."hello"); +>res1 : readonly string[] +>fn1(..."hello") : readonly string[] +>fn1 : (...args: T) => T +>..."hello" : string +>"hello" : "hello" + +const res2 = fn1(...itNum); +>res2 : readonly number[] +>fn1(...itNum) : readonly number[] +>fn1 : (...args: T) => T +>...itNum : number +>itNum : Iterable + +const res3 = fn1(true, ..."hello"); +>res3 : readonly [true, ...string[]] +>fn1(true, ..."hello") : readonly [true, ...string[]] +>fn1 : (...args: T) => T +>true : true +>..."hello" : string +>"hello" : "hello" + +const res4 = fn1(true, ...itNum); +>res4 : readonly [true, ...number[]] +>fn1(true, ...itNum) : readonly [true, ...number[]] +>fn1 : (...args: T) => T +>true : true +>...itNum : number +>itNum : Iterable + +// repro from #52781 +declare function foo(...args: T): T; +>foo : (...args: T) => T +>args : T + +const p1 = foo(..."hello"); +>p1 : string[] +>foo(..."hello") : string[] +>foo : (...args: T) => T +>..."hello" : string +>"hello" : "hello" + +const p2 = foo(...itNum); +>p2 : number[] +>foo(...itNum) : number[] +>foo : (...args: T) => T +>...itNum : number +>itNum : Iterable + +const p3 = foo(true, ..."hello"); +>p3 : [boolean, ...string[]] +>foo(true, ..."hello") : [boolean, ...string[]] +>foo : (...args: T) => T +>true : true +>..."hello" : string +>"hello" : "hello" + +const p4 = foo(true, ...itNum); +>p4 : [boolean, ...number[]] +>foo(true, ...itNum) : [boolean, ...number[]] +>foo : (...args: T) => T +>true : true +>...itNum : number +>itNum : Iterable + diff --git a/tests/cases/compiler/argumentsSpreadRestIterables.tsx b/tests/cases/compiler/argumentsSpreadRestIterables.tsx new file mode 100644 index 0000000000000..a68aec01fa9c4 --- /dev/null +++ b/tests/cases/compiler/argumentsSpreadRestIterables.tsx @@ -0,0 +1,22 @@ +// @strict: true +// @noEmit: true +// @target: es5, esnext + +declare const itNum: Iterable + +;(function(...rest) {})(...itNum) +;(function(a, ...rest) {})('', ...itNum) +;(function(a, ...rest) {})('', true, ...itNum) + +declare function fn1(...args: T): T; +const res1 = fn1(..."hello"); +const res2 = fn1(...itNum); +const res3 = fn1(true, ..."hello"); +const res4 = fn1(true, ...itNum); + +// repro from #52781 +declare function foo(...args: T): T; +const p1 = foo(..."hello"); +const p2 = foo(...itNum); +const p3 = foo(true, ..."hello"); +const p4 = foo(true, ...itNum);