diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9014e2f43f91d..2a9d3f152afe7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -827,6 +827,7 @@ namespace ts { /** Key is "/path/to/a.ts|/path/to/b.ts". */ let amalgamatedDuplicates: Map | undefined; const reverseMappedCache = createMap(); + let inInferTypeForHomomorphicMappedType = false; let ambientModulesCache: Symbol[] | undefined; /** * List of every ambient module with a "*" wildcard. @@ -18267,12 +18268,16 @@ namespace ts { * variable T[P] (i.e. we treat the type T[P] as the type variable we're inferring for). */ function inferTypeForHomomorphicMappedType(source: Type, target: MappedType, constraint: IndexType): Type | undefined { + if (inInferTypeForHomomorphicMappedType) { + return undefined; + } const key = source.id + "," + target.id + "," + constraint.id; if (reverseMappedCache.has(key)) { return reverseMappedCache.get(key); } - reverseMappedCache.set(key, undefined); + inInferTypeForHomomorphicMappedType = true; const type = createReverseMappedType(source, target, constraint); + inInferTypeForHomomorphicMappedType = false; reverseMappedCache.set(key, type); return type; } diff --git a/tests/baselines/reference/recursiveReverseMappedType.js b/tests/baselines/reference/recursiveReverseMappedType.js new file mode 100644 index 0000000000000..1d500b9ee65f6 --- /dev/null +++ b/tests/baselines/reference/recursiveReverseMappedType.js @@ -0,0 +1,32 @@ +//// [recursiveReverseMappedType.ts] +// Repro from #38198 + +type Recur = ( + T extends (unknown[]) ? {} : { [K in keyof T]?: Recur } +) | ['marker', ...Recur[]]; + +function join(l: Recur[]): Recur { + return ['marker', ...l]; +} + +function a(l: Recur[]): void { + const x: Recur | undefined = join(l); +} + + +//// [recursiveReverseMappedType.js] +"use strict"; +// Repro from #38198 +var __spreadArrays = (this && this.__spreadArrays) || function () { + for (var s = 0, i = 0, il = arguments.length; i < il; i++) s += arguments[i].length; + for (var r = Array(s), k = 0, i = 0; i < il; i++) + for (var a = arguments[i], j = 0, jl = a.length; j < jl; j++, k++) + r[k] = a[j]; + return r; +}; +function join(l) { + return __spreadArrays(['marker'], l); +} +function a(l) { + var x = join(l); +} diff --git a/tests/baselines/reference/recursiveReverseMappedType.symbols b/tests/baselines/reference/recursiveReverseMappedType.symbols new file mode 100644 index 0000000000000..6e4a9cb5b0c8f --- /dev/null +++ b/tests/baselines/reference/recursiveReverseMappedType.symbols @@ -0,0 +1,47 @@ +=== tests/cases/compiler/recursiveReverseMappedType.ts === +// Repro from #38198 + +type Recur = ( +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11)) + + T extends (unknown[]) ? {} : { [K in keyof T]?: Recur } +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11)) +>K : Symbol(K, Decl(recursiveReverseMappedType.ts, 3, 36)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11)) +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11)) +>K : Symbol(K, Decl(recursiveReverseMappedType.ts, 3, 36)) + +) | ['marker', ...Recur[]]; +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 2, 11)) + +function join(l: Recur[]): Recur { +>join : Symbol(join, Decl(recursiveReverseMappedType.ts, 4, 30)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14)) +>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 6, 17)) +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14)) +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 6, 14)) + + return ['marker', ...l]; +>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 6, 17)) +} + +function a(l: Recur[]): void { +>a : Symbol(a, Decl(recursiveReverseMappedType.ts, 8, 1)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11)) +>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 10, 14)) +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11)) + + const x: Recur | undefined = join(l); +>x : Symbol(x, Decl(recursiveReverseMappedType.ts, 11, 9)) +>Recur : Symbol(Recur, Decl(recursiveReverseMappedType.ts, 0, 0)) +>T : Symbol(T, Decl(recursiveReverseMappedType.ts, 10, 11)) +>join : Symbol(join, Decl(recursiveReverseMappedType.ts, 4, 30)) +>l : Symbol(l, Decl(recursiveReverseMappedType.ts, 10, 14)) +} + diff --git a/tests/baselines/reference/recursiveReverseMappedType.types b/tests/baselines/reference/recursiveReverseMappedType.types new file mode 100644 index 0000000000000..a39e242d90be8 --- /dev/null +++ b/tests/baselines/reference/recursiveReverseMappedType.types @@ -0,0 +1,31 @@ +=== tests/cases/compiler/recursiveReverseMappedType.ts === +// Repro from #38198 + +type Recur = ( +>Recur : Recur + + T extends (unknown[]) ? {} : { [K in keyof T]?: Recur } +) | ['marker', ...Recur[]]; + +function join(l: Recur[]): Recur { +>join : (l: Recur[]) => Recur +>l : Recur[] + + return ['marker', ...l]; +>['marker', ...l] : ["marker", ...Recur[]] +>'marker' : "marker" +>...l : Recur +>l : Recur[] +} + +function a(l: Recur[]): void { +>a : (l: Recur[]) => void +>l : Recur[] + + const x: Recur | undefined = join(l); +>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[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined; }) | ["marker", ...Recur[]] | undefined +>join(l) : Recur +>join : (l: Recur[]) => Recur +>l : Recur[] +} + diff --git a/tests/cases/compiler/recursiveReverseMappedType.ts b/tests/cases/compiler/recursiveReverseMappedType.ts new file mode 100644 index 0000000000000..89c80095f7927 --- /dev/null +++ b/tests/cases/compiler/recursiveReverseMappedType.ts @@ -0,0 +1,15 @@ +// @strict: true + +// Repro from #38198 + +type Recur = ( + T extends (unknown[]) ? {} : { [K in keyof T]?: Recur } +) | ['marker', ...Recur[]]; + +function join(l: Recur[]): Recur { + return ['marker', ...l]; +} + +function a(l: Recur[]): void { + const x: Recur | undefined = join(l); +}