Skip to content

Commit 7c512fb

Browse files
authored
Merge pull request #25817 from Microsoft/fixGenericRestTypes
Fix generic rest types
2 parents e103692 + 949f8d2 commit 7c512fb

File tree

5 files changed

+166
-22
lines changed

5 files changed

+166
-22
lines changed

src/compiler/checker.ts

+22-22
Original file line numberDiff line numberDiff line change
@@ -10514,9 +10514,9 @@ namespace ts {
1051410514
}
1051510515

1051610516
const sourceCount = getParameterCount(source);
10517-
const sourceRestTypeParameter = getRestTypeParameter(source);
10518-
const targetRestTypeParameter = sourceRestTypeParameter ? getRestTypeParameter(target) : undefined;
10519-
if (sourceRestTypeParameter && !(targetRestTypeParameter && sourceCount === targetCount)) {
10517+
const sourceGenericRestType = getGenericRestType(source);
10518+
const targetGenericRestType = sourceGenericRestType ? getGenericRestType(target) : undefined;
10519+
if (sourceGenericRestType && !(targetGenericRestType && sourceCount === targetCount)) {
1052010520
return Ternary.False;
1052110521
}
1052210522

@@ -10545,8 +10545,8 @@ namespace ts {
1054510545
const paramCount = Math.max(sourceCount, targetCount);
1054610546
const lastIndex = paramCount - 1;
1054710547
for (let i = 0; i < paramCount; i++) {
10548-
const sourceType = i === lastIndex && sourceRestTypeParameter || getTypeAtPosition(source, i);
10549-
const targetType = i === lastIndex && targetRestTypeParameter || getTypeAtPosition(target, i);
10548+
const sourceType = i === lastIndex && sourceGenericRestType || getTypeAtPosition(source, i);
10549+
const targetType = i === lastIndex && targetGenericRestType || getTypeAtPosition(target, i);
1055010550
// In order to ensure that any generic type Foo<T> is at least co-variant with respect to T no matter
1055110551
// how Foo uses T, we need to relate parameters bi-variantly (given that parameters are input positions,
1055210552
// they naturally relate only contra-variantly). However, if the source and target parameters both have
@@ -12821,13 +12821,13 @@ namespace ts {
1282112821
sourceHasRest ? targetCount :
1282212822
targetHasRest ? sourceCount :
1282312823
Math.min(sourceCount, targetCount);
12824-
const targetRestTypeVariable = getRestTypeParameter(target);
12825-
const paramCount = targetRestTypeVariable ? Math.min(targetCount - 1, maxCount) : maxCount;
12824+
const targetGenericRestType = getGenericRestType(target);
12825+
const paramCount = targetGenericRestType ? Math.min(targetCount - 1, maxCount) : maxCount;
1282612826
for (let i = 0; i < paramCount; i++) {
1282712827
callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i));
1282812828
}
12829-
if (targetRestTypeVariable) {
12830-
callback(getRestTypeAtPosition(source, paramCount), targetRestTypeVariable);
12829+
if (targetGenericRestType) {
12830+
callback(getRestTypeAtPosition(source, paramCount), targetGenericRestType);
1283112831
}
1283212832
}
1283312833

@@ -18398,8 +18398,8 @@ namespace ts {
1839818398
// We perform two passes over the arguments. In the first pass we infer from all arguments, but use
1839918399
// wildcards for all context sensitive function expressions.
1840018400
const effectiveArgCount = getEffectiveArgumentCount(node, args, signature);
18401-
const restTypeParameter = getRestTypeParameter(signature);
18402-
const argCount = restTypeParameter ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount;
18401+
const genericRestType = getGenericRestType(signature);
18402+
const argCount = genericRestType ? Math.min(getParameterCount(signature) - 1, effectiveArgCount) : effectiveArgCount;
1840318403
for (let i = 0; i < argCount; i++) {
1840418404
const arg = getEffectiveArgument(node, args, i);
1840518405
// If the effective argument is 'undefined', then it is an argument that is present but is synthetic.
@@ -18418,9 +18418,9 @@ namespace ts {
1841818418
}
1841918419
}
1842018420

18421-
if (restTypeParameter) {
18422-
const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, restTypeParameter, context);
18423-
inferTypes(context.inferences, spreadType, restTypeParameter);
18421+
if (genericRestType) {
18422+
const spreadType = getSpreadArgumentType(node, args, argCount, effectiveArgCount, genericRestType, context);
18423+
inferTypes(context.inferences, spreadType, genericRestType);
1842418424
}
1842518425

1842618426
// In the second pass we visit only context sensitive arguments, and only those that aren't excluded, this
@@ -19170,9 +19170,9 @@ namespace ts {
1917019170
}
1917119171
const isJavascript = isInJavaScriptFile(candidate.declaration);
1917219172
candidate = getSignatureInstantiation(candidate, typeArgumentTypes, isJavascript);
19173-
// If the original signature has a rest type parameter, instantiation may produce a
19173+
// If the original signature has a generic rest type, instantiation may produce a
1917419174
// signature with different arity and we need to perform another arity check.
19175-
if (getRestTypeParameter(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
19175+
if (getGenericRestType(originalCandidate) && !hasCorrectArity(node, args!, candidate, signatureHelpTrailingComma)) {
1917619176
candidateForArgumentArityError = candidate;
1917719177
break;
1917819178
}
@@ -20104,9 +20104,9 @@ namespace ts {
2010420104
const paramCount = getParameterCount(source);
2010520105
const hasRest = hasEffectiveRestParameter(source);
2010620106
if (hasRest && pos === paramCount - 1) {
20107-
const restTypeVariable = getRestTypeParameter(source);
20108-
if (restTypeVariable) {
20109-
return restTypeVariable;
20107+
const genericRestType = getGenericRestType(source);
20108+
if (genericRestType) {
20109+
return genericRestType;
2011020110
}
2011120111
}
2011220112
const start = hasRest ? Math.min(pos, paramCount - 1) : pos;
@@ -20156,11 +20156,11 @@ namespace ts {
2015620156
return signature.minArgumentCount;
2015720157
}
2015820158

20159-
function getRestTypeParameter(signature: Signature) {
20159+
function getGenericRestType(signature: Signature) {
2016020160
if (signature.hasRestParameter) {
2016120161
const restType = getTypeOfSymbol(signature.parameters[signature.parameters.length - 1]);
20162-
if (restType.flags & TypeFlags.TypeParameter) {
20163-
return <TypeParameter>restType;
20162+
if (restType.flags & TypeFlags.Instantiable) {
20163+
return restType;
2016420164
}
2016520165
}
2016620166
return undefined;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//// [genericRestTypes.ts]
2+
// Repro from #25793
3+
4+
// Gets the parameters of a function type as a tuple
5+
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
6+
// Removes the first element from a tuple
7+
type Tail<T extends any[]> = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never;
8+
9+
type MyFunctionType = (foo: number, bar: string) => boolean;
10+
11+
type Explicit = (...args: Tail<Parameters<MyFunctionType>>) => ReturnType<MyFunctionType>; // (bar: string) => boolean
12+
13+
type Bind1<T extends (head: any, ...tail: any[]) => any> = (...args: Tail<Parameters<T>>) => ReturnType<T>;
14+
type Generic = Bind1<MyFunctionType>; // (bar: string) => boolean
15+
16+
17+
//// [genericRestTypes.js]
18+
"use strict";
19+
// Repro from #25793
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/compiler/genericRestTypes.ts ===
2+
// Repro from #25793
3+
4+
// Gets the parameters of a function type as a tuple
5+
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
6+
>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0))
7+
>T : Symbol(T, Decl(genericRestTypes.ts, 3, 16))
8+
>args : Symbol(args, Decl(genericRestTypes.ts, 3, 27))
9+
>T : Symbol(T, Decl(genericRestTypes.ts, 3, 16))
10+
>args : Symbol(args, Decl(genericRestTypes.ts, 3, 64))
11+
>U : Symbol(U, Decl(genericRestTypes.ts, 3, 78))
12+
>U : Symbol(U, Decl(genericRestTypes.ts, 3, 78))
13+
14+
// Removes the first element from a tuple
15+
type Tail<T extends any[]> = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never;
16+
>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101))
17+
>T : Symbol(T, Decl(genericRestTypes.ts, 5, 10))
18+
>args : Symbol(args, Decl(genericRestTypes.ts, 5, 31))
19+
>T : Symbol(T, Decl(genericRestTypes.ts, 5, 10))
20+
>head : Symbol(head, Decl(genericRestTypes.ts, 5, 61))
21+
>tail : Symbol(tail, Decl(genericRestTypes.ts, 5, 71))
22+
>U : Symbol(U, Decl(genericRestTypes.ts, 5, 86))
23+
>U : Symbol(U, Decl(genericRestTypes.ts, 5, 86))
24+
25+
type MyFunctionType = (foo: number, bar: string) => boolean;
26+
>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110))
27+
>foo : Symbol(foo, Decl(genericRestTypes.ts, 7, 23))
28+
>bar : Symbol(bar, Decl(genericRestTypes.ts, 7, 35))
29+
30+
type Explicit = (...args: Tail<Parameters<MyFunctionType>>) => ReturnType<MyFunctionType>; // (bar: string) => boolean
31+
>Explicit : Symbol(Explicit, Decl(genericRestTypes.ts, 7, 60))
32+
>args : Symbol(args, Decl(genericRestTypes.ts, 9, 17))
33+
>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101))
34+
>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0))
35+
>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110))
36+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
37+
>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110))
38+
39+
type Bind1<T extends (head: any, ...tail: any[]) => any> = (...args: Tail<Parameters<T>>) => ReturnType<T>;
40+
>Bind1 : Symbol(Bind1, Decl(genericRestTypes.ts, 9, 90))
41+
>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11))
42+
>head : Symbol(head, Decl(genericRestTypes.ts, 11, 22))
43+
>tail : Symbol(tail, Decl(genericRestTypes.ts, 11, 32))
44+
>args : Symbol(args, Decl(genericRestTypes.ts, 11, 60))
45+
>Tail : Symbol(Tail, Decl(genericRestTypes.ts, 3, 101))
46+
>Parameters : Symbol(Parameters, Decl(genericRestTypes.ts, 0, 0))
47+
>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11))
48+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
49+
>T : Symbol(T, Decl(genericRestTypes.ts, 11, 11))
50+
51+
type Generic = Bind1<MyFunctionType>; // (bar: string) => boolean
52+
>Generic : Symbol(Generic, Decl(genericRestTypes.ts, 11, 107))
53+
>Bind1 : Symbol(Bind1, Decl(genericRestTypes.ts, 9, 90))
54+
>MyFunctionType : Symbol(MyFunctionType, Decl(genericRestTypes.ts, 5, 110))
55+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
=== tests/cases/compiler/genericRestTypes.ts ===
2+
// Repro from #25793
3+
4+
// Gets the parameters of a function type as a tuple
5+
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
6+
>Parameters : Parameters<T>
7+
>T : T
8+
>args : any[]
9+
>T : T
10+
>args : U
11+
>U : U
12+
>U : U
13+
14+
// Removes the first element from a tuple
15+
type Tail<T extends any[]> = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never;
16+
>Tail : Tail<T>
17+
>T : T
18+
>args : T
19+
>T : T
20+
>head : any
21+
>tail : U
22+
>U : U
23+
>U : U
24+
25+
type MyFunctionType = (foo: number, bar: string) => boolean;
26+
>MyFunctionType : MyFunctionType
27+
>foo : number
28+
>bar : string
29+
30+
type Explicit = (...args: Tail<Parameters<MyFunctionType>>) => ReturnType<MyFunctionType>; // (bar: string) => boolean
31+
>Explicit : Explicit
32+
>args : [string]
33+
>Tail : Tail<T>
34+
>Parameters : Parameters<T>
35+
>MyFunctionType : MyFunctionType
36+
>ReturnType : ReturnType<T>
37+
>MyFunctionType : MyFunctionType
38+
39+
type Bind1<T extends (head: any, ...tail: any[]) => any> = (...args: Tail<Parameters<T>>) => ReturnType<T>;
40+
>Bind1 : Bind1<T>
41+
>T : T
42+
>head : any
43+
>tail : any[]
44+
>args : Tail<Parameters<T>>
45+
>Tail : Tail<T>
46+
>Parameters : Parameters<T>
47+
>T : T
48+
>ReturnType : ReturnType<T>
49+
>T : T
50+
51+
type Generic = Bind1<MyFunctionType>; // (bar: string) => boolean
52+
>Generic : Bind1<MyFunctionType>
53+
>Bind1 : Bind1<T>
54+
>MyFunctionType : MyFunctionType
55+
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @strict: true
2+
3+
// Repro from #25793
4+
5+
// Gets the parameters of a function type as a tuple
6+
type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;
7+
// Removes the first element from a tuple
8+
type Tail<T extends any[]> = ((...args: T) => any) extends ((head: any, ...tail: infer U) => any) ? U : never;
9+
10+
type MyFunctionType = (foo: number, bar: string) => boolean;
11+
12+
type Explicit = (...args: Tail<Parameters<MyFunctionType>>) => ReturnType<MyFunctionType>; // (bar: string) => boolean
13+
14+
type Bind1<T extends (head: any, ...tail: any[]) => any> = (...args: Tail<Parameters<T>>) => ReturnType<T>;
15+
type Generic = Bind1<MyFunctionType>; // (bar: string) => boolean

0 commit comments

Comments
 (0)