Skip to content

Commit 61910e8

Browse files
authored
Fix missing constraints for parenthesized infer T (microsoft#40406)
* add tests * consider parenthesized types in getInferredTypeParameterConstraint * update tests
1 parent 10b240c commit 61910e8

File tree

6 files changed

+167
-2
lines changed

6 files changed

+167
-2
lines changed

src/compiler/checker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11941,12 +11941,12 @@ namespace ts {
1194111941
// (such as 'Foo<infer T>'), T's constraint is inferred from the constraint of the
1194211942
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
1194311943
// present, we form an intersection of the inferred constraint types.
11944-
const grandParent = declaration.parent.parent;
11944+
const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent);
1194511945
if (grandParent.kind === SyntaxKind.TypeReference) {
1194611946
const typeReference = <TypeReferenceNode>grandParent;
1194711947
const typeParameters = getTypeParametersForTypeReference(typeReference);
1194811948
if (typeParameters) {
11949-
const index = typeReference.typeArguments!.indexOf(<TypeNode>declaration.parent);
11949+
const index = typeReference.typeArguments!.indexOf(<TypeNode>childTypeParameter);
1195011950
if (index < typeParameters.length) {
1195111951
const declaredConstraint = getConstraintOfTypeParameter(typeParameters[index]);
1195211952
if (declaredConstraint) {

src/compiler/utilities.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2730,6 +2730,20 @@ namespace ts {
27302730
return walkUp(node, SyntaxKind.ParenthesizedExpression);
27312731
}
27322732

2733+
/**
2734+
* Walks up parenthesized types.
2735+
* It returns both the outermost parenthesized type and its parent.
2736+
* If given node is not a parenthesiezd type, undefined is return as the former.
2737+
*/
2738+
export function walkUpParenthesizedTypesAndGetParentAndChild(node: Node): [ParenthesizedTypeNode | undefined, Node] {
2739+
let child: ParenthesizedTypeNode | undefined;
2740+
while (node && node.kind === SyntaxKind.ParenthesizedType) {
2741+
child = <ParenthesizedTypeNode>node;
2742+
node = node.parent;
2743+
}
2744+
return [child, node];
2745+
}
2746+
27332747
export function skipParentheses(node: Expression): Expression;
27342748
export function skipParentheses(node: Node): Node;
27352749
export function skipParentheses(node: Node): Node {
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [inferTInParentheses.ts]
2+
type F1 = (num: [number]) => void;
3+
type IsNumber<T extends number> = T;
4+
5+
type T1 = F1 extends (...args: (infer T)) => void ? T : never;
6+
type T2 = F1 extends (args: [...(infer T)]) => void ? T : never;
7+
type T3<T> = T extends IsNumber<(infer N)> ? true : false;
8+
9+
type T4 = F1 extends (...args: ((infer T))) => void ? T : never;
10+
type T5 = F1 extends (args: [...((infer T))]) => void ? T : never;
11+
type T6<T> = T extends IsNumber<((infer N))> ? true : false;
12+
13+
type T7 = F1 extends (...args: ((((infer T))))) => void ? T : never;
14+
type T8 = F1 extends (args: [...((((infer T))))]) => void ? T : never;
15+
type T9<T> = T extends IsNumber<((((infer N))))> ? true : false;
16+
17+
//// [inferTInParentheses.js]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
=== tests/cases/compiler/inferTInParentheses.ts ===
2+
type F1 = (num: [number]) => void;
3+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
4+
>num : Symbol(num, Decl(inferTInParentheses.ts, 0, 11))
5+
6+
type IsNumber<T extends number> = T;
7+
>IsNumber : Symbol(IsNumber, Decl(inferTInParentheses.ts, 0, 34))
8+
>T : Symbol(T, Decl(inferTInParentheses.ts, 1, 14))
9+
>T : Symbol(T, Decl(inferTInParentheses.ts, 1, 14))
10+
11+
type T1 = F1 extends (...args: (infer T)) => void ? T : never;
12+
>T1 : Symbol(T1, Decl(inferTInParentheses.ts, 1, 36))
13+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
14+
>args : Symbol(args, Decl(inferTInParentheses.ts, 3, 22))
15+
>T : Symbol(T, Decl(inferTInParentheses.ts, 3, 37))
16+
>T : Symbol(T, Decl(inferTInParentheses.ts, 3, 37))
17+
18+
type T2 = F1 extends (args: [...(infer T)]) => void ? T : never;
19+
>T2 : Symbol(T2, Decl(inferTInParentheses.ts, 3, 62))
20+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
21+
>args : Symbol(args, Decl(inferTInParentheses.ts, 4, 22))
22+
>T : Symbol(T, Decl(inferTInParentheses.ts, 4, 38))
23+
>T : Symbol(T, Decl(inferTInParentheses.ts, 4, 38))
24+
25+
type T3<T> = T extends IsNumber<(infer N)> ? true : false;
26+
>T3 : Symbol(T3, Decl(inferTInParentheses.ts, 4, 64))
27+
>T : Symbol(T, Decl(inferTInParentheses.ts, 5, 8))
28+
>T : Symbol(T, Decl(inferTInParentheses.ts, 5, 8))
29+
>IsNumber : Symbol(IsNumber, Decl(inferTInParentheses.ts, 0, 34))
30+
>N : Symbol(N, Decl(inferTInParentheses.ts, 5, 38))
31+
32+
type T4 = F1 extends (...args: ((infer T))) => void ? T : never;
33+
>T4 : Symbol(T4, Decl(inferTInParentheses.ts, 5, 58))
34+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
35+
>args : Symbol(args, Decl(inferTInParentheses.ts, 7, 22))
36+
>T : Symbol(T, Decl(inferTInParentheses.ts, 7, 38))
37+
>T : Symbol(T, Decl(inferTInParentheses.ts, 7, 38))
38+
39+
type T5 = F1 extends (args: [...((infer T))]) => void ? T : never;
40+
>T5 : Symbol(T5, Decl(inferTInParentheses.ts, 7, 64))
41+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
42+
>args : Symbol(args, Decl(inferTInParentheses.ts, 8, 22))
43+
>T : Symbol(T, Decl(inferTInParentheses.ts, 8, 39))
44+
>T : Symbol(T, Decl(inferTInParentheses.ts, 8, 39))
45+
46+
type T6<T> = T extends IsNumber<((infer N))> ? true : false;
47+
>T6 : Symbol(T6, Decl(inferTInParentheses.ts, 8, 66))
48+
>T : Symbol(T, Decl(inferTInParentheses.ts, 9, 8))
49+
>T : Symbol(T, Decl(inferTInParentheses.ts, 9, 8))
50+
>IsNumber : Symbol(IsNumber, Decl(inferTInParentheses.ts, 0, 34))
51+
>N : Symbol(N, Decl(inferTInParentheses.ts, 9, 39))
52+
53+
type T7 = F1 extends (...args: ((((infer T))))) => void ? T : never;
54+
>T7 : Symbol(T7, Decl(inferTInParentheses.ts, 9, 60))
55+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
56+
>args : Symbol(args, Decl(inferTInParentheses.ts, 11, 22))
57+
>T : Symbol(T, Decl(inferTInParentheses.ts, 11, 40))
58+
>T : Symbol(T, Decl(inferTInParentheses.ts, 11, 40))
59+
60+
type T8 = F1 extends (args: [...((((infer T))))]) => void ? T : never;
61+
>T8 : Symbol(T8, Decl(inferTInParentheses.ts, 11, 68))
62+
>F1 : Symbol(F1, Decl(inferTInParentheses.ts, 0, 0))
63+
>args : Symbol(args, Decl(inferTInParentheses.ts, 12, 22))
64+
>T : Symbol(T, Decl(inferTInParentheses.ts, 12, 41))
65+
>T : Symbol(T, Decl(inferTInParentheses.ts, 12, 41))
66+
67+
type T9<T> = T extends IsNumber<((((infer N))))> ? true : false;
68+
>T9 : Symbol(T9, Decl(inferTInParentheses.ts, 12, 70))
69+
>T : Symbol(T, Decl(inferTInParentheses.ts, 13, 8))
70+
>T : Symbol(T, Decl(inferTInParentheses.ts, 13, 8))
71+
>IsNumber : Symbol(IsNumber, Decl(inferTInParentheses.ts, 0, 34))
72+
>N : Symbol(N, Decl(inferTInParentheses.ts, 13, 41))
73+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/inferTInParentheses.ts ===
2+
type F1 = (num: [number]) => void;
3+
>F1 : F1
4+
>num : [number]
5+
6+
type IsNumber<T extends number> = T;
7+
>IsNumber : T
8+
9+
type T1 = F1 extends (...args: (infer T)) => void ? T : never;
10+
>T1 : [num: [number]]
11+
>args : T
12+
13+
type T2 = F1 extends (args: [...(infer T)]) => void ? T : never;
14+
>T2 : [number]
15+
>args : [...T]
16+
17+
type T3<T> = T extends IsNumber<(infer N)> ? true : false;
18+
>T3 : T3<T>
19+
>true : true
20+
>false : false
21+
22+
type T4 = F1 extends (...args: ((infer T))) => void ? T : never;
23+
>T4 : [num: [number]]
24+
>args : T
25+
26+
type T5 = F1 extends (args: [...((infer T))]) => void ? T : never;
27+
>T5 : [number]
28+
>args : [...T]
29+
30+
type T6<T> = T extends IsNumber<((infer N))> ? true : false;
31+
>T6 : T6<T>
32+
>true : true
33+
>false : false
34+
35+
type T7 = F1 extends (...args: ((((infer T))))) => void ? T : never;
36+
>T7 : [num: [number]]
37+
>args : T
38+
39+
type T8 = F1 extends (args: [...((((infer T))))]) => void ? T : never;
40+
>T8 : [number]
41+
>args : [...T]
42+
43+
type T9<T> = T extends IsNumber<((((infer N))))> ? true : false;
44+
>T9 : T9<T>
45+
>true : true
46+
>false : false
47+
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
type F1 = (num: [number]) => void;
2+
type IsNumber<T extends number> = T;
3+
4+
type T1 = F1 extends (...args: (infer T)) => void ? T : never;
5+
type T2 = F1 extends (args: [...(infer T)]) => void ? T : never;
6+
type T3<T> = T extends IsNumber<(infer N)> ? true : false;
7+
8+
type T4 = F1 extends (...args: ((infer T))) => void ? T : never;
9+
type T5 = F1 extends (args: [...((infer T))]) => void ? T : never;
10+
type T6<T> = T extends IsNumber<((infer N))> ? true : false;
11+
12+
type T7 = F1 extends (...args: ((((infer T))))) => void ? T : never;
13+
type T8 = F1 extends (args: [...((((infer T))))]) => void ? T : never;
14+
type T9<T> = T extends IsNumber<((((infer N))))> ? true : false;

0 commit comments

Comments
 (0)