mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Fix recursive type inference (#53396)
This commit is contained in:
parent
bace6897e2
commit
3d2c3442db
@ -22911,7 +22911,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
return type.symbol;
|
||||
}
|
||||
if (isTupleType(type)) {
|
||||
return type;
|
||||
return type.target;
|
||||
}
|
||||
}
|
||||
if (type.flags & TypeFlags.TypeParameter) {
|
||||
@ -24273,8 +24273,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
let inferencePriority: number = InferencePriority.MaxValue;
|
||||
let allowComplexConstraintInference = true;
|
||||
let visited: Map<string, number>;
|
||||
let sourceStack: object[];
|
||||
let targetStack: object[];
|
||||
let sourceStack: Type[];
|
||||
let targetStack: Type[];
|
||||
let expandingFlags = ExpandingFlags.None;
|
||||
inferFromTypes(originalSource, originalTarget);
|
||||
|
||||
@ -24530,20 +24530,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);
|
||||
|
||||
@ -0,0 +1,54 @@
|
||||
tests/cases/compiler/inferFromNestedSameShapeTuple.ts(42,5): error TS2322: Type 'T1<U>' is not assignable to type 'T2<U>'.
|
||||
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> = X extends [[infer Y, ...infer _], ...infer __] ? Y : never;
|
||||
|
||||
type R = Magic<[[number]]>
|
||||
|
||||
// repro #52722
|
||||
|
||||
type Recursive<Id> = {
|
||||
id: Id
|
||||
children: readonly Recursive<Id>[]
|
||||
}
|
||||
|
||||
declare function getIds<Id>(items: readonly Recursive<Id>[]): Id[];
|
||||
|
||||
const items = [{
|
||||
id: 'a',
|
||||
children: [{
|
||||
id: 'b',
|
||||
children: []
|
||||
}]
|
||||
}] as const satisfies readonly Recursive<string>[]
|
||||
|
||||
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<T> = [number, T1<{ x: T }>];
|
||||
type T2<T> = [42, T2<{ x: T }>];
|
||||
|
||||
function qq<U>(x: T1<U>, y: T2<U>) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
~
|
||||
!!! error TS2322: Type 'T1<U>' is not assignable to type 'T2<U>'.
|
||||
!!! 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'.
|
||||
}
|
||||
|
||||
@ -84,3 +84,38 @@ const foo2 = getIds([{
|
||||
}] as const)
|
||||
>const : Symbol(const)
|
||||
|
||||
// Repro from comment in #49226
|
||||
|
||||
type T1<T> = [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<T> = [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<U>(x: T1<U>, y: T2<U>) {
|
||||
>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))
|
||||
}
|
||||
|
||||
|
||||
@ -84,3 +84,30 @@ const foo2 = getIds([{
|
||||
|
||||
}]
|
||||
}] as const)
|
||||
|
||||
// Repro from comment in #49226
|
||||
|
||||
type T1<T> = [number, T1<{ x: T }>];
|
||||
>T1 : T1<T>
|
||||
>x : T
|
||||
|
||||
type T2<T> = [42, T2<{ x: T }>];
|
||||
>T2 : T2<T>
|
||||
>x : T
|
||||
|
||||
function qq<U>(x: T1<U>, y: T2<U>) {
|
||||
>qq : <U>(x: T1<U>, y: T2<U>) => void
|
||||
>x : T1<U>
|
||||
>y : T2<U>
|
||||
|
||||
x = y;
|
||||
>x = y : T2<U>
|
||||
>x : T1<U>
|
||||
>y : T2<U>
|
||||
|
||||
y = x; // Error
|
||||
>y = x : T1<U>
|
||||
>y : T2<U>
|
||||
>x : T1<U>
|
||||
}
|
||||
|
||||
|
||||
@ -146,7 +146,7 @@ tests/cases/compiler/recursiveConditionalTypes.ts(169,5): error TS2322: Type 'nu
|
||||
|
||||
declare let z: Box2<Box2<string>>;
|
||||
|
||||
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
|
||||
foo(z); // string
|
||||
|
||||
// Intersect tuple element types
|
||||
|
||||
|
||||
@ -92,7 +92,7 @@ declare function foo<T>(x: Box1<Box1<T>>): T;
|
||||
|
||||
declare let z: Box2<Box2<string>>;
|
||||
|
||||
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<string>
|
||||
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;
|
||||
|
||||
@ -362,7 +362,7 @@ declare let z: Box2<Box2<string>>;
|
||||
>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))
|
||||
|
||||
|
||||
@ -248,8 +248,8 @@ declare function foo<T>(x: Box1<Box1<T>>): T;
|
||||
declare let z: Box2<Box2<string>>;
|
||||
>z : Box2<Box2<string>>
|
||||
|
||||
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 : <T>(x: Box1<Box1<T>>) => T
|
||||
>z : Box2<Box2<string>>
|
||||
|
||||
|
||||
@ -33,4 +33,14 @@ const foo2 = getIds([{
|
||||
id: 'b',
|
||||
children: []
|
||||
}]
|
||||
}] as const)
|
||||
}] as const)
|
||||
|
||||
// Repro from comment in #49226
|
||||
|
||||
type T1<T> = [number, T1<{ x: T }>];
|
||||
type T2<T> = [42, T2<{ x: T }>];
|
||||
|
||||
function qq<U>(x: T1<U>, y: T2<U>) {
|
||||
x = y;
|
||||
y = x; // Error
|
||||
}
|
||||
|
||||
@ -95,7 +95,7 @@ declare function foo<T>(x: Box1<Box1<T>>): T;
|
||||
|
||||
declare let z: Box2<Box2<string>>;
|
||||
|
||||
foo(z); // unknown, but ideally would be string (requires unique recursion ID for each type reference)
|
||||
foo(z); // string
|
||||
|
||||
// Intersect tuple element types
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user