diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 6f5e6e44c191a..a5c100b05401a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18127,7 +18127,7 @@ namespace ts { } function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority: InferencePriority = 0, contravariant = false) { - let symbolStack: Symbol[]; + let symbolOrTypeStack: (Symbol | Type)[]; let visited: Map; let bivariant = false; let propagationType: Type; @@ -18566,15 +18566,15 @@ namespace ts { // its symbol with the instance side which would lead to false positives. const isNonConstructorObject = target.flags & TypeFlags.Object && !(getObjectFlags(target) & ObjectFlags.Anonymous && target.symbol && target.symbol.flags & SymbolFlags.Class); - const symbol = isNonConstructorObject ? target.symbol : undefined; - if (symbol) { - if (contains(symbolStack, symbol)) { + const symbolOrType = isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined; + if (symbolOrType) { + if (contains(symbolOrTypeStack, symbolOrType)) { inferencePriority = InferencePriority.Circularity; return; } - (symbolStack || (symbolStack = [])).push(symbol); + (symbolOrTypeStack || (symbolOrTypeStack = [])).push(symbolOrType); inferFromObjectTypesWorker(source, target); - symbolStack.pop(); + symbolOrTypeStack.pop(); } else { inferFromObjectTypesWorker(source, target); diff --git a/tests/baselines/reference/recursiveTupleTypeInference.errors.txt b/tests/baselines/reference/recursiveTupleTypeInference.errors.txt new file mode 100644 index 0000000000000..6bf76030c8d6b --- /dev/null +++ b/tests/baselines/reference/recursiveTupleTypeInference.errors.txt @@ -0,0 +1,36 @@ +tests/cases/compiler/recursiveTupleTypeInference.ts(23,5): error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'. + Types of property 'b' are incompatible. + Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. + Type '"number"' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. + + +==== tests/cases/compiler/recursiveTupleTypeInference.ts (1 errors) ==== + // Repro from #37475 + + export type A = "number" | "null" | A[]; + + export type F = null extends T + ? [F>, "null"] + : T extends number + ? "number" + : never; + + export type G = { [k in keyof T]: F }; + + interface K { + b: number | null; + } + + const gK: { [key in keyof K]: A } = { b: ["number", "null"] }; + + function foo(g: G): T { + return {} as any; + } + + foo(gK); + ~~ +!!! error TS2345: Argument of type '{ b: A; }' is not assignable to parameter of type 'G<{ b: unknown; }>'. +!!! error TS2345: Types of property 'b' are incompatible. +!!! error TS2345: Type 'A' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. +!!! error TS2345: Type '"number"' is not assignable to type '[[[[[[[[[[[[any, "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"], "null"]'. + \ No newline at end of file diff --git a/tests/baselines/reference/recursiveTupleTypeInference.js b/tests/baselines/reference/recursiveTupleTypeInference.js new file mode 100644 index 0000000000000..0f521e0d885d8 --- /dev/null +++ b/tests/baselines/reference/recursiveTupleTypeInference.js @@ -0,0 +1,35 @@ +//// [recursiveTupleTypeInference.ts] +// Repro from #37475 + +export type A = "number" | "null" | A[]; + +export type F = null extends T + ? [F>, "null"] + : T extends number + ? "number" + : never; + +export type G = { [k in keyof T]: F }; + +interface K { + b: number | null; +} + +const gK: { [key in keyof K]: A } = { b: ["number", "null"] }; + +function foo(g: G): T { + return {} as any; +} + +foo(gK); + + +//// [recursiveTupleTypeInference.js] +"use strict"; +// Repro from #37475 +exports.__esModule = true; +var gK = { b: ["number", "null"] }; +function foo(g) { + return {}; +} +foo(gK); diff --git a/tests/baselines/reference/recursiveTupleTypeInference.symbols b/tests/baselines/reference/recursiveTupleTypeInference.symbols new file mode 100644 index 0000000000000..fdb19cab50989 --- /dev/null +++ b/tests/baselines/reference/recursiveTupleTypeInference.symbols @@ -0,0 +1,61 @@ +=== tests/cases/compiler/recursiveTupleTypeInference.ts === +// Repro from #37475 + +export type A = "number" | "null" | A[]; +>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0)) +>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0)) + +export type F = null extends T +>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14)) + + ? [F>, "null"] +>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40)) +>NonNullable : Symbol(NonNullable, Decl(lib.es5.d.ts, --, --)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14)) + + : T extends number +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 4, 14)) + + ? "number" + : never; + +export type G = { [k in keyof T]: F }; +>G : Symbol(G, Decl(recursiveTupleTypeInference.ts, 8, 12)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14)) +>k : Symbol(k, Decl(recursiveTupleTypeInference.ts, 10, 22)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14)) +>F : Symbol(F, Decl(recursiveTupleTypeInference.ts, 2, 40)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 10, 14)) +>k : Symbol(k, Decl(recursiveTupleTypeInference.ts, 10, 22)) + +interface K { +>K : Symbol(K, Decl(recursiveTupleTypeInference.ts, 10, 47)) + + b: number | null; +>b : Symbol(K.b, Decl(recursiveTupleTypeInference.ts, 12, 13)) +} + +const gK: { [key in keyof K]: A } = { b: ["number", "null"] }; +>gK : Symbol(gK, Decl(recursiveTupleTypeInference.ts, 16, 5)) +>key : Symbol(key, Decl(recursiveTupleTypeInference.ts, 16, 13)) +>K : Symbol(K, Decl(recursiveTupleTypeInference.ts, 10, 47)) +>A : Symbol(A, Decl(recursiveTupleTypeInference.ts, 0, 0)) +>b : Symbol(b, Decl(recursiveTupleTypeInference.ts, 16, 37)) + +function foo(g: G): T { +>foo : Symbol(foo, Decl(recursiveTupleTypeInference.ts, 16, 62)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13)) +>g : Symbol(g, Decl(recursiveTupleTypeInference.ts, 18, 16)) +>G : Symbol(G, Decl(recursiveTupleTypeInference.ts, 8, 12)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13)) +>T : Symbol(T, Decl(recursiveTupleTypeInference.ts, 18, 13)) + + return {} as any; +} + +foo(gK); +>foo : Symbol(foo, Decl(recursiveTupleTypeInference.ts, 16, 62)) +>gK : Symbol(gK, Decl(recursiveTupleTypeInference.ts, 16, 5)) + diff --git a/tests/baselines/reference/recursiveTupleTypeInference.types b/tests/baselines/reference/recursiveTupleTypeInference.types new file mode 100644 index 0000000000000..bc2c5373dcf1b --- /dev/null +++ b/tests/baselines/reference/recursiveTupleTypeInference.types @@ -0,0 +1,46 @@ +=== tests/cases/compiler/recursiveTupleTypeInference.ts === +// Repro from #37475 + +export type A = "number" | "null" | A[]; +>A : A + +export type F = null extends T +>F : F +>null : null + + ? [F>, "null"] + : T extends number + ? "number" + : never; + +export type G = { [k in keyof T]: F }; +>G : G + +interface K { + b: number | null; +>b : number | null +>null : null +} + +const gK: { [key in keyof K]: A } = { b: ["number", "null"] }; +>gK : { b: A; } +>{ b: ["number", "null"] } : { b: ("number" | "null")[]; } +>b : ("number" | "null")[] +>["number", "null"] : ("number" | "null")[] +>"number" : "number" +>"null" : "null" + +function foo(g: G): T { +>foo : (g: G) => T +>g : G + + return {} as any; +>{} as any : any +>{} : {} +} + +foo(gK); +>foo(gK) : { b: unknown; } +>foo : (g: G) => T +>gK : { b: A; } + diff --git a/tests/cases/compiler/recursiveTupleTypeInference.ts b/tests/cases/compiler/recursiveTupleTypeInference.ts new file mode 100644 index 0000000000000..9d6bfc4d3342f --- /dev/null +++ b/tests/cases/compiler/recursiveTupleTypeInference.ts @@ -0,0 +1,25 @@ +// @strict: true + +// Repro from #37475 + +export type A = "number" | "null" | A[]; + +export type F = null extends T + ? [F>, "null"] + : T extends number + ? "number" + : never; + +export type G = { [k in keyof T]: F }; + +interface K { + b: number | null; +} + +const gK: { [key in keyof K]: A } = { b: ["number", "null"] }; + +function foo(g: G): T { + return {} as any; +} + +foo(gK);