diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c520f2caed01a..8cdff48f5c5bb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -22891,7 +22891,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type.symbol; } if (isTupleType(type)) { - return type; + return type.target; } } if (type.flags & TypeFlags.TypeParameter) { @@ -24253,8 +24253,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { let inferencePriority: number = InferencePriority.MaxValue; let allowComplexConstraintInference = true; let visited: Map; - let sourceStack: object[]; - let targetStack: object[]; + let sourceStack: Type[]; + let targetStack: Type[]; let expandingFlags = ExpandingFlags.None; inferFromTypes(originalSource, originalTarget); @@ -24510,20 +24510,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // We stop inferring and report a circularity if we encounter duplicate recursion identities on both // the source side and the target side. const saveExpandingFlags = expandingFlags; - const sourceIdentity = getRecursionIdentity(source); - const targetIdentity = getRecursionIdentity(target); - if (contains(sourceStack, sourceIdentity)) expandingFlags |= ExpandingFlags.Source; - if (contains(targetStack, targetIdentity)) expandingFlags |= ExpandingFlags.Target; + (sourceStack ??= []).push(source); + (targetStack ??= []).push(target); + if (isDeeplyNestedType(source, sourceStack, sourceStack.length, 2)) expandingFlags |= ExpandingFlags.Source; + if (isDeeplyNestedType(target, targetStack, targetStack.length, 2)) expandingFlags |= ExpandingFlags.Target; if (expandingFlags !== ExpandingFlags.Both) { - (sourceStack || (sourceStack = [])).push(sourceIdentity); - (targetStack || (targetStack = [])).push(targetIdentity); action(source, target); - targetStack.pop(); - sourceStack.pop(); } else { inferencePriority = InferencePriority.Circularity; } + targetStack.pop(); + sourceStack.pop(); expandingFlags = saveExpandingFlags; visited.set(key, inferencePriority); inferencePriority = Math.min(inferencePriority, saveInferencePriority); diff --git a/tests/baselines/reference/inferFromNestedSameShapeTuple.errors.txt b/tests/baselines/reference/inferFromNestedSameShapeTuple.errors.txt new file mode 100644 index 0000000000000..4bdbb2b7b9f2c --- /dev/null +++ b/tests/baselines/reference/inferFromNestedSameShapeTuple.errors.txt @@ -0,0 +1,54 @@ +tests/cases/compiler/inferFromNestedSameShapeTuple.ts(42,5): error TS2322: Type 'T1' is not assignable to type 'T2'. + Type at position 0 in source is not compatible with type at position 0 in target. + Type 'number' is not assignable to type '42'. + + +==== tests/cases/compiler/inferFromNestedSameShapeTuple.ts (1 errors) ==== + // repro #48524 + + type Magic = X extends [[infer Y, ...infer _], ...infer __] ? Y : never; + + type R = Magic<[[number]]> + + // repro #52722 + + type Recursive = { + id: Id + children: readonly Recursive[] + } + + declare function getIds(items: readonly Recursive[]): Id[]; + + const items = [{ + id: 'a', + children: [{ + id: 'b', + children: [] + }] + }] as const satisfies readonly Recursive[] + + const foo = getIds(items) + + // variant with a fresh argument + const foo2 = getIds([{ + id: 'a', + children: [{ + id: 'b', + children: [] + }] + }] as const) + + // Repro from comment in #49226 + + type T1 = [number, T1<{ x: T }>]; + type T2 = [42, T2<{ x: T }>]; + + function qq(x: T1, y: T2) { + x = y; + y = x; // Error + ~ +!!! error TS2322: Type 'T1' is not assignable to type 'T2'. +!!! error TS2322: Type at position 0 in source is not compatible with type at position 0 in target. +!!! error TS2322: Type 'number' is not assignable to type '42'. + } + \ No newline at end of file diff --git a/tests/baselines/reference/inferFromNestedSameShapeTuple.symbols b/tests/baselines/reference/inferFromNestedSameShapeTuple.symbols index 789febc6172e0..0e0c2bd3b45ed 100644 --- a/tests/baselines/reference/inferFromNestedSameShapeTuple.symbols +++ b/tests/baselines/reference/inferFromNestedSameShapeTuple.symbols @@ -84,3 +84,38 @@ const foo2 = getIds([{ }] as const) >const : Symbol(const) +// Repro from comment in #49226 + +type T1 = [number, T1<{ x: T }>]; +>T1 : Symbol(T1, Decl(inferFromNestedSameShapeTuple.ts, 32, 12)) +>T : Symbol(T, Decl(inferFromNestedSameShapeTuple.ts, 36, 8)) +>T1 : Symbol(T1, Decl(inferFromNestedSameShapeTuple.ts, 32, 12)) +>x : Symbol(x, Decl(inferFromNestedSameShapeTuple.ts, 36, 26)) +>T : Symbol(T, Decl(inferFromNestedSameShapeTuple.ts, 36, 8)) + +type T2 = [42, T2<{ x: T }>]; +>T2 : Symbol(T2, Decl(inferFromNestedSameShapeTuple.ts, 36, 36)) +>T : Symbol(T, Decl(inferFromNestedSameShapeTuple.ts, 37, 8)) +>T2 : Symbol(T2, Decl(inferFromNestedSameShapeTuple.ts, 36, 36)) +>x : Symbol(x, Decl(inferFromNestedSameShapeTuple.ts, 37, 22)) +>T : Symbol(T, Decl(inferFromNestedSameShapeTuple.ts, 37, 8)) + +function qq(x: T1, y: T2) { +>qq : Symbol(qq, Decl(inferFromNestedSameShapeTuple.ts, 37, 32)) +>U : Symbol(U, Decl(inferFromNestedSameShapeTuple.ts, 39, 12)) +>x : Symbol(x, Decl(inferFromNestedSameShapeTuple.ts, 39, 15)) +>T1 : Symbol(T1, Decl(inferFromNestedSameShapeTuple.ts, 32, 12)) +>U : Symbol(U, Decl(inferFromNestedSameShapeTuple.ts, 39, 12)) +>y : Symbol(y, Decl(inferFromNestedSameShapeTuple.ts, 39, 24)) +>T2 : Symbol(T2, Decl(inferFromNestedSameShapeTuple.ts, 36, 36)) +>U : Symbol(U, Decl(inferFromNestedSameShapeTuple.ts, 39, 12)) + + x = y; +>x : Symbol(x, Decl(inferFromNestedSameShapeTuple.ts, 39, 15)) +>y : Symbol(y, Decl(inferFromNestedSameShapeTuple.ts, 39, 24)) + + y = x; // Error +>y : Symbol(y, Decl(inferFromNestedSameShapeTuple.ts, 39, 24)) +>x : Symbol(x, Decl(inferFromNestedSameShapeTuple.ts, 39, 15)) +} + diff --git a/tests/baselines/reference/inferFromNestedSameShapeTuple.types b/tests/baselines/reference/inferFromNestedSameShapeTuple.types index 244d5f183906e..c737b8f577cee 100644 --- a/tests/baselines/reference/inferFromNestedSameShapeTuple.types +++ b/tests/baselines/reference/inferFromNestedSameShapeTuple.types @@ -84,3 +84,30 @@ const foo2 = getIds([{ }] }] as const) + +// Repro from comment in #49226 + +type T1 = [number, T1<{ x: T }>]; +>T1 : T1 +>x : T + +type T2 = [42, T2<{ x: T }>]; +>T2 : T2 +>x : T + +function qq(x: T1, y: T2) { +>qq : (x: T1, y: T2) => void +>x : T1 +>y : T2 + + x = y; +>x = y : T2 +>x : T1 +>y : T2 + + y = x; // Error +>y = x : T1 +>y : T2 +>x : T1 +} + diff --git a/tests/baselines/reference/recursiveConditionalTypes.errors.txt b/tests/baselines/reference/recursiveConditionalTypes.errors.txt index f82908dd7fba8..a5033ec71d410 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.errors.txt +++ b/tests/baselines/reference/recursiveConditionalTypes.errors.txt @@ -146,7 +146,7 @@ tests/cases/compiler/recursiveConditionalTypes.ts(169,5): error TS2322: Type 'nu declare let z: Box2>; - foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) + foo(z); // string // Intersect tuple element types diff --git a/tests/baselines/reference/recursiveConditionalTypes.js b/tests/baselines/reference/recursiveConditionalTypes.js index a978a0bfa2a9f..9f62615ac3568 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.js +++ b/tests/baselines/reference/recursiveConditionalTypes.js @@ -92,7 +92,7 @@ declare function foo(x: Box1>): T; declare let z: Box2>; -foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) +foo(z); // string // Intersect tuple element types @@ -192,7 +192,7 @@ unbox(b3); // InfBox unbox({ value: { value: { value: { value: { value: { value: 5 } } } } } }); // number unbox(b4); // { value: { value: typeof b4 }} unbox({ value: { value: { get value() { return this; } } } }); // { readonly value: ... } -foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) +foo(z); // string function f20(x, y) { x = y; y = x; diff --git a/tests/baselines/reference/recursiveConditionalTypes.symbols b/tests/baselines/reference/recursiveConditionalTypes.symbols index dffbbd124f0c0..803d142e52936 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.symbols +++ b/tests/baselines/reference/recursiveConditionalTypes.symbols @@ -362,7 +362,7 @@ declare let z: Box2>; >Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 86, 28)) >Box2 : Symbol(Box2, Decl(recursiveConditionalTypes.ts, 86, 28)) -foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) +foo(z); // string >foo : Symbol(foo, Decl(recursiveConditionalTypes.ts, 87, 28)) >z : Symbol(z, Decl(recursiveConditionalTypes.ts, 91, 11)) diff --git a/tests/baselines/reference/recursiveConditionalTypes.types b/tests/baselines/reference/recursiveConditionalTypes.types index 83fb2e4b65fcc..170889ff18ae6 100644 --- a/tests/baselines/reference/recursiveConditionalTypes.types +++ b/tests/baselines/reference/recursiveConditionalTypes.types @@ -248,8 +248,8 @@ declare function foo(x: Box1>): T; declare let z: Box2>; >z : Box2> -foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) ->foo(z) : unknown +foo(z); // string +>foo(z) : string >foo : (x: Box1>) => T >z : Box2> diff --git a/tests/cases/compiler/inferFromNestedSameShapeTuple.ts b/tests/cases/compiler/inferFromNestedSameShapeTuple.ts index 60c83e484c4a1..d41e337969c32 100644 --- a/tests/cases/compiler/inferFromNestedSameShapeTuple.ts +++ b/tests/cases/compiler/inferFromNestedSameShapeTuple.ts @@ -33,4 +33,14 @@ const foo2 = getIds([{ id: 'b', children: [] }] -}] as const) \ No newline at end of file +}] as const) + +// Repro from comment in #49226 + +type T1 = [number, T1<{ x: T }>]; +type T2 = [42, T2<{ x: T }>]; + +function qq(x: T1, y: T2) { + x = y; + y = x; // Error +} diff --git a/tests/cases/compiler/recursiveConditionalTypes.ts b/tests/cases/compiler/recursiveConditionalTypes.ts index 6810a37ca072f..44a28b863e483 100644 --- a/tests/cases/compiler/recursiveConditionalTypes.ts +++ b/tests/cases/compiler/recursiveConditionalTypes.ts @@ -95,7 +95,7 @@ declare function foo(x: Box1>): T; declare let z: Box2>; -foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference) +foo(z); // string // Intersect tuple element types