Fixed spreading iterables into arguments list directly into rest-only signatures (#52838)

This commit is contained in:
Mateusz Burzyński
2023-03-02 18:09:54 +01:00
committed by GitHub
parent 7bee9c55b1
commit 6b71882c4c
7 changed files with 413 additions and 3 deletions

View File

@@ -32223,19 +32223,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)) {

View File

@@ -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<number>
~~~~~~~~
!!! error TS2304: Cannot find name 'Iterable'.
;(function(...rest) {})(...itNum)
;(function(a, ...rest) {})('', ...itNum)
;(function(a, ...rest) {})('', true, ...itNum)
declare function fn1<const T extends readonly unknown[]>(...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<T extends unknown[]>(...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);

View File

@@ -0,0 +1,70 @@
=== tests/cases/compiler/argumentsSpreadRestIterables.tsx ===
declare const itNum: Iterable<number>
>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<const T extends readonly unknown[]>(...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<T extends unknown[]>(...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))

View File

@@ -0,0 +1,102 @@
=== tests/cases/compiler/argumentsSpreadRestIterables.tsx ===
declare const itNum: Iterable<number>
>itNum : Iterable<number>
;(function(...rest) {})(...itNum)
>(function(...rest) {})(...itNum) : void
>(function(...rest) {}) : (...rest: Iterable<number>) => void
>function(...rest) {} : (...rest: Iterable<number>) => void
>rest : Iterable<number>
>...itNum : Iterable<number>
>itNum : Iterable<number>
;(function(a, ...rest) {})('', ...itNum)
>(function(a, ...rest) {})('', ...itNum) : void
>(function(a, ...rest) {}) : (a: string, ...rest: Iterable<number>) => void
>function(a, ...rest) {} : (a: string, ...rest: Iterable<number>) => void
>a : string
>rest : Iterable<number>
>'' : ""
>...itNum : Iterable<number>
>itNum : Iterable<number>
;(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<number>
>itNum : Iterable<number>
declare function fn1<const T extends readonly unknown[]>(...args: T): T;
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>args : T
const res1 = fn1(..."hello");
>res1 : readonly any[]
>fn1(..."hello") : readonly any[]
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>..."hello" : any
>"hello" : "hello"
const res2 = fn1(...itNum);
>res2 : Iterable<number>
>fn1(...itNum) : Iterable<number>
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>...itNum : Iterable<number>
>itNum : Iterable<number>
const res3 = fn1(true, ..."hello");
>res3 : readonly [true, ...any[]]
>fn1(true, ..."hello") : readonly [true, ...any[]]
>fn1 : <const T extends readonly unknown[]>(...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 : <const T extends readonly unknown[]>(...args: T) => T
>true : true
>...itNum : Iterable<number>
>itNum : Iterable<number>
// repro from #52781
declare function foo<T extends unknown[]>(...args: T): T;
>foo : <T extends unknown[]>(...args: T) => T
>args : T
const p1 = foo(..."hello");
>p1 : any[]
>foo(..."hello") : any[]
>foo : <T extends unknown[]>(...args: T) => T
>..."hello" : any
>"hello" : "hello"
const p2 = foo(...itNum);
>p2 : Iterable<number>
>foo(...itNum) : Iterable<number>
>foo : <T extends unknown[]>(...args: T) => T
>...itNum : Iterable<number>
>itNum : Iterable<number>
const p3 = foo(true, ..."hello");
>p3 : [boolean, ...any[]]
>foo(true, ..."hello") : [boolean, ...any[]]
>foo : <T extends unknown[]>(...args: T) => T
>true : true
>..."hello" : any
>"hello" : "hello"
const p4 = foo(true, ...itNum);
>p4 : [boolean, ...any[]]
>foo(true, ...itNum) : [boolean, ...any[]]
>foo : <T extends unknown[]>(...args: T) => T
>true : true
>...itNum : Iterable<number>
>itNum : Iterable<number>

View File

@@ -0,0 +1,70 @@
=== tests/cases/compiler/argumentsSpreadRestIterables.tsx ===
declare const itNum: Iterable<number>
>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<const T extends readonly unknown[]>(...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<T extends unknown[]>(...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))

View File

@@ -0,0 +1,102 @@
=== tests/cases/compiler/argumentsSpreadRestIterables.tsx ===
declare const itNum: Iterable<number>
>itNum : Iterable<number>
;(function(...rest) {})(...itNum)
>(function(...rest) {})(...itNum) : void
>(function(...rest) {}) : (...rest: number[]) => void
>function(...rest) {} : (...rest: number[]) => void
>rest : number[]
>...itNum : number
>itNum : Iterable<number>
;(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<number>
;(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<number>
declare function fn1<const T extends readonly unknown[]>(...args: T): T;
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>args : T
const res1 = fn1(..."hello");
>res1 : readonly string[]
>fn1(..."hello") : readonly string[]
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>..."hello" : string
>"hello" : "hello"
const res2 = fn1(...itNum);
>res2 : readonly number[]
>fn1(...itNum) : readonly number[]
>fn1 : <const T extends readonly unknown[]>(...args: T) => T
>...itNum : number
>itNum : Iterable<number>
const res3 = fn1(true, ..."hello");
>res3 : readonly [true, ...string[]]
>fn1(true, ..."hello") : readonly [true, ...string[]]
>fn1 : <const T extends readonly unknown[]>(...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 : <const T extends readonly unknown[]>(...args: T) => T
>true : true
>...itNum : number
>itNum : Iterable<number>
// repro from #52781
declare function foo<T extends unknown[]>(...args: T): T;
>foo : <T extends unknown[]>(...args: T) => T
>args : T
const p1 = foo(..."hello");
>p1 : string[]
>foo(..."hello") : string[]
>foo : <T extends unknown[]>(...args: T) => T
>..."hello" : string
>"hello" : "hello"
const p2 = foo(...itNum);
>p2 : number[]
>foo(...itNum) : number[]
>foo : <T extends unknown[]>(...args: T) => T
>...itNum : number
>itNum : Iterable<number>
const p3 = foo(true, ..."hello");
>p3 : [boolean, ...string[]]
>foo(true, ..."hello") : [boolean, ...string[]]
>foo : <T extends unknown[]>(...args: T) => T
>true : true
>..."hello" : string
>"hello" : "hello"
const p4 = foo(true, ...itNum);
>p4 : [boolean, ...number[]]
>foo(true, ...itNum) : [boolean, ...number[]]
>foo : <T extends unknown[]>(...args: T) => T
>true : true
>...itNum : number
>itNum : Iterable<number>

View File

@@ -0,0 +1,22 @@
// @strict: true
// @noEmit: true
// @target: es5, esnext
declare const itNum: Iterable<number>
;(function(...rest) {})(...itNum)
;(function(a, ...rest) {})('', ...itNum)
;(function(a, ...rest) {})('', true, ...itNum)
declare function fn1<const T extends readonly unknown[]>(...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<T extends unknown[]>(...args: T): T;
const p1 = foo(..."hello");
const p2 = foo(...itNum);
const p3 = foo(true, ..."hello");
const p4 = foo(true, ...itNum);