Skip to content

Commit 658de7f

Browse files
authored
Merge pull request microsoft#21134 from Microsoft/fix-recursive-mapped-type-infinite-recursion
Fix recursive mapped type infinite recursion
2 parents 538d7e7 + 2b630e9 commit 658de7f

8 files changed

Lines changed: 93 additions & 8 deletions

src/compiler/checker.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -530,6 +530,7 @@ namespace ts {
530530
ResolvedBaseConstructorType,
531531
DeclaredType,
532532
ResolvedReturnType,
533+
ResolvedBaseConstraint,
533534
}
534535

535536
const enum CheckMode {
@@ -4254,7 +4255,7 @@ namespace ts {
42544255
return -1;
42554256
}
42564257

4257-
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type {
4258+
function hasType(target: TypeSystemEntity, propertyName: TypeSystemPropertyName): Type | boolean {
42584259
if (propertyName === TypeSystemPropertyName.Type) {
42594260
return getSymbolLinks(<Symbol>target).type;
42604261
}
@@ -4267,6 +4268,10 @@ namespace ts {
42674268
if (propertyName === TypeSystemPropertyName.ResolvedReturnType) {
42684269
return (<Signature>target).resolvedReturnType;
42694270
}
4271+
if (propertyName === TypeSystemPropertyName.ResolvedBaseConstraint) {
4272+
const bc = (<TypeParameter | UnionOrIntersectionType>target).resolvedBaseConstraint;
4273+
return bc && bc !== circularConstraintType;
4274+
}
42704275

42714276
Debug.fail("Unhandled TypeSystemPropertyName " + propertyName);
42724277
}
@@ -6500,23 +6505,23 @@ namespace ts {
65006505
* circularly references the type variable.
65016506
*/
65026507
function getResolvedBaseConstraint(type: TypeVariable | UnionOrIntersectionType): Type {
6503-
let typeStack: Type[];
65046508
let circular: boolean;
65056509
if (!type.resolvedBaseConstraint) {
6506-
typeStack = [];
65076510
const constraint = getBaseConstraint(type);
65086511
type.resolvedBaseConstraint = circular ? circularConstraintType : getTypeWithThisArgument(constraint || noConstraintType, type);
65096512
}
65106513
return type.resolvedBaseConstraint;
65116514

65126515
function getBaseConstraint(t: Type): Type {
6513-
if (contains(typeStack, t)) {
6516+
if (!pushTypeResolution(t, TypeSystemPropertyName.ResolvedBaseConstraint)) {
65146517
circular = true;
65156518
return undefined;
65166519
}
6517-
typeStack.push(t);
65186520
const result = computeBaseConstraint(t);
6519-
typeStack.pop();
6521+
if (!popTypeResolution()) {
6522+
circular = true;
6523+
return undefined;
6524+
}
65206525
return result;
65216526
}
65226527

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts(2,32): error TS2313: Type parameter 'P' has a circular constraint.
2+
3+
4+
==== tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts (1 errors) ====
5+
// #17847
6+
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
7+
~
8+
!!! error TS2313: Type parameter 'P' has a circular constraint.
9+
n += v[k];
10+
}
11+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//// [incorrectRecursiveMappedTypeConstraint.ts]
2+
// #17847
3+
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
4+
n += v[k];
5+
}
6+
7+
8+
//// [incorrectRecursiveMappedTypeConstraint.js]
9+
// #17847
10+
function sum(n, v, k) {
11+
n += v[k];
12+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts ===
2+
// #17847
3+
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
4+
>sum : Symbol(sum, Decl(incorrectRecursiveMappedTypeConstraint.ts, 0, 0))
5+
>T : Symbol(T, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 13))
6+
>P : Symbol(P, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 26))
7+
>T : Symbol(T, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 13))
8+
>K : Symbol(K, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 44))
9+
>T : Symbol(T, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 13))
10+
>n : Symbol(n, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 64))
11+
>v : Symbol(v, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 74))
12+
>T : Symbol(T, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 13))
13+
>k : Symbol(k, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 80))
14+
>K : Symbol(K, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 44))
15+
16+
n += v[k];
17+
>n : Symbol(n, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 64))
18+
>v : Symbol(v, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 74))
19+
>k : Symbol(k, Decl(incorrectRecursiveMappedTypeConstraint.ts, 1, 80))
20+
}
21+
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
=== tests/cases/compiler/incorrectRecursiveMappedTypeConstraint.ts ===
2+
// #17847
3+
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
4+
>sum : <T extends { [x: string]: number; }, K extends keyof T>(n: number, v: T, k: K) => void
5+
>T : T
6+
>P : P
7+
>T : T
8+
>K : K
9+
>T : T
10+
>n : number
11+
>v : T
12+
>T : T
13+
>k : K
14+
>K : K
15+
16+
n += v[k];
17+
>n += v[k] : number
18+
>n : number
19+
>v[k] : T[K]
20+
>v : T
21+
>k : K
22+
}
23+
Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,34 @@
11
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(3,6): error TS2456: Type alias 'Recurse' circularly references itself.
2+
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(4,11): error TS2313: Type parameter 'K' has a circular constraint.
23
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(7,6): error TS2456: Type alias 'Recurse1' circularly references itself.
4+
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(8,11): error TS2313: Type parameter 'K' has a circular constraint.
35
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(11,6): error TS2456: Type alias 'Recurse2' circularly references itself.
6+
tests/cases/conformance/types/mapped/recursiveMappedTypes.ts(12,11): error TS2313: Type parameter 'K' has a circular constraint.
47

58

6-
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (3 errors) ====
9+
==== tests/cases/conformance/types/mapped/recursiveMappedTypes.ts (6 errors) ====
710
// Recursive mapped types simply appear empty
811

912
type Recurse = {
1013
~~~~~~~
1114
!!! error TS2456: Type alias 'Recurse' circularly references itself.
1215
[K in keyof Recurse]: Recurse[K]
16+
~~~~~~~~~~~~~
17+
!!! error TS2313: Type parameter 'K' has a circular constraint.
1318
}
1419

1520
type Recurse1 = {
1621
~~~~~~~~
1722
!!! error TS2456: Type alias 'Recurse1' circularly references itself.
1823
[K in keyof Recurse2]: Recurse2[K]
24+
~~~~~~~~~~~~~~
25+
!!! error TS2313: Type parameter 'K' has a circular constraint.
1926
}
2027

2128
type Recurse2 = {
2229
~~~~~~~~
2330
!!! error TS2456: Type alias 'Recurse2' circularly references itself.
2431
[K in keyof Recurse1]: Recurse1[K]
32+
~~~~~~~~~~~~~~
33+
!!! error TS2313: Type parameter 'K' has a circular constraint.
2534
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// #17847
2+
function sum<T extends { [P in T]: number }, K extends keyof T>(n: number, v: T, k: K) {
3+
n += v[k];
4+
}

0 commit comments

Comments
 (0)