Skip to content

Commit a0ebd2c

Browse files
authored
Guard against recursion in inferTypeForHomomorphicMappedType (microsoft#38224)
* Guard against recursion in inferTypeForHomomorphicMappedType * Add regression test
1 parent 16d2eb7 commit a0ebd2c

File tree

5 files changed

+131
-1
lines changed

5 files changed

+131
-1
lines changed

src/compiler/checker.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,7 @@ namespace ts {
827827
/** Key is "/path/to/a.ts|/path/to/b.ts". */
828828
let amalgamatedDuplicates: Map<DuplicateInfoForFiles> | undefined;
829829
const reverseMappedCache = createMap<Type | undefined>();
830+
let inInferTypeForHomomorphicMappedType = false;
830831
let ambientModulesCache: Symbol[] | undefined;
831832
/**
832833
* List of every ambient module with a "*" wildcard.
@@ -18366,12 +18367,16 @@ namespace ts {
1836618367
* variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for).
1836718368
*/
1836818369
function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined {
18370+
if (inInferTypeForHomomorphicMappedType) {
18371+
return undefined;
18372+
}
1836918373
const key = source.id + "," + target.id + "," + constraint.id;
1837018374
if (reverseMappedCache.has(key)) {
1837118375
return reverseMappedCache.get(key);
1837218376
}
18373-
reverseMappedCache.set(key, undefined);
18377+
inInferTypeForHomomorphicMappedType = true;
1837418378
const type = createReverseMappedType(source, target, constraint);
18379+
inInferTypeForHomomorphicMappedType = false;
1837518380
reverseMappedCache.set(key, type);
1837618381
return type;
1837718382
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//// [recursiveReverseMappedType.ts]
2+
// Repro from #38198
3+
4+
type Recur<T> = (
5+
T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]> }
6+
) | ['marker', ...Recur<T>[]];
7+
8+
function join<T>(l: Recur<T>[]): Recur<T> {
9+
return ['marker', ...l];
10+
}
11+
12+
function a<T>(l: Recur<T>[]): void {
13+
const x: Recur<T> | undefined = join(l);
14+
}
15+
16+
17+
//// [recursiveReverseMappedType.js]
18+
"use strict";
19+
// Repro from #38198
20+
var __spreadArrays = (this && this.__spreadArrays) || function () {
21+
for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length;
22+
for (var r = Array(s), k = 0, i = 0; i < il; i++)
23+
for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++)
24+
r[k] = a[j];
25+
return r;
26+
};
27+
function join(l) {
28+
return __spreadArrays(['marker'], l);
29+
}
30+
function a(l) {
31+
var x = join(l);
32+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
=== tests/cases/compiler/recursiveReverseMappedType.ts ===
2+
// Repro from #38198
3+
4+
type Recur<T> = (
5+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
6+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11))
7+
8+
T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]> }
9+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11))
10+
>K : Symbol(K, Decl(recursiveReverseMappedType.ts, 3, 36))
11+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11))
12+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
13+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11))
14+
>K : Symbol(K, Decl(recursiveReverseMappedType.ts, 3, 36))
15+
16+
) | ['marker', ...Recur<T>[]];
17+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
18+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11))
19+
20+
function join<T>(l: Recur<T>[]): Recur<T> {
21+
>join : Symbol(join, Decl(recursiveReverseMappedType.ts, 4, 30))
22+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14))
23+
>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 6, 17))
24+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
25+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14))
26+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
27+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14))
28+
29+
return ['marker', ...l];
30+
>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 6, 17))
31+
}
32+
33+
function a<T>(l: Recur<T>[]): void {
34+
>a : Symbol(a, Decl(recursiveReverseMappedType.ts, 8, 1))
35+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11))
36+
>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 10, 14))
37+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
38+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11))
39+
40+
const x: Recur<T> | undefined = join(l);
41+
>x : Symbol(x, Decl(recursiveReverseMappedType.ts, 11, 9))
42+
>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0))
43+
>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11))
44+
>join : Symbol(join, Decl(recursiveReverseMappedType.ts, 4, 30))
45+
>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 10, 14))
46+
}
47+
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
=== tests/cases/compiler/recursiveReverseMappedType.ts ===
2+
// Repro from #38198
3+
4+
type Recur<T> = (
5+
>Recur : Recur<T>
6+
7+
T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]> }
8+
) | ['marker', ...Recur<T>[]];
9+
10+
function join<T>(l: Recur<T>[]): Recur<T> {
11+
>join : <T>(l: Recur<T>[]) => Recur<T>
12+
>l : Recur<T>[]
13+
14+
return ['marker', ...l];
15+
>['marker', ...l] : ["marker", ...Recur<T>[]]
16+
>'marker' : "marker"
17+
>...l : Recur<T>
18+
>l : Recur<T>[]
19+
}
20+
21+
function a<T>(l: Recur<T>[]): void {
22+
>a : <T>(l: Recur<T>[]) => void
23+
>l : Recur<T>[]
24+
25+
const x: Recur<T> | undefined = join(l);
26+
>x : (T extends unknown[] ? {} : { [K in keyof T]?: (T[K] extends unknown[] ? {} : { [K in keyof T[K]]?: (T[K][K] extends unknown[] ? {} : { [K in keyof T[K][K]]?: (T[K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K]]?: (T[K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K]]?: (T[K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K]]?: (T[K][K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K][K]]?: (T[K][K][K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K][K][K]]?: (T[K][K][K][K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K][K][K][K]]?: (T[K][K][K][K][K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K][K][K][K][K]]?: (T[K][K][K][K][K][K][K][K][K][K] extends unknown[] ? {} : { [K in keyof T[K][K][K][K][K][K][K][K][K][K]]?: (T[K][K][K][K][K][K][K][K][K][K][K] extends unknown[] ? {} : any) | ["marker", ...Recur<T[K][K][K][K][K][K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K][K]>[]] | undefined; }) | ["marker", ...Recur<T[K]>[]] | undefined; }) | ["marker", ...Recur<T>[]] | undefined
27+
>join(l) : Recur<T>
28+
>join : <T>(l: Recur<T>[]) => Recur<T>
29+
>l : Recur<T>[]
30+
}
31+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// @strict: true
2+
3+
// Repro from #38198
4+
5+
type Recur<T> = (
6+
T extends (unknown[]) ? {} : { [K in keyof T]?: Recur<T[K]> }
7+
) | ['marker', ...Recur<T>[]];
8+
9+
function join<T>(l: Recur<T>[]): Recur<T> {
10+
return ['marker', ...l];
11+
}
12+
13+
function a<T>(l: Recur<T>[]): void {
14+
const x: Recur<T> | undefined = join(l);
15+
}

0 commit comments

Comments
 (0)