mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-21 08:25:43 -05:00
Handle recursive type references up to a certain level of expansion in inference (#38011)
* Handle recursive type references up to a certain level of expansion in inference * Move object cases into the same conditional branch
This commit is contained in:
@@ -18090,15 +18090,32 @@ namespace ts {
|
||||
// In addition, this will also detect when an indexed access has been chained off of 5 or more times (which is essentially
|
||||
// the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding false positives
|
||||
// for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
|
||||
// It also detects when a recursive type reference has expanded 5 or more times, eg, if the true branch of
|
||||
// `type A<T> = null extends T ? [A<NonNullable<T>>] : [T]`
|
||||
// has expanded into `[A<NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>>>>>>]`
|
||||
// in such cases we need to terminate the expansion, and we do so here.
|
||||
function isDeeplyNestedType(type: Type, stack: Type[], depth: number): boolean {
|
||||
// We track all object types that have an associated symbol (representing the origin of the type)
|
||||
if (depth >= 5 && type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) {
|
||||
const symbol = type.symbol;
|
||||
if (symbol) {
|
||||
if (depth >= 5 && type.flags & TypeFlags.Object) {
|
||||
if (!isObjectOrArrayLiteralType(type)) {
|
||||
const symbol = type.symbol;
|
||||
if (symbol) {
|
||||
let count = 0;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
const t = stack[i];
|
||||
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
|
||||
count++;
|
||||
if (count >= 5) return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (getObjectFlags(type) && ObjectFlags.Reference && !!(type as TypeReference).node) {
|
||||
const root = (type as TypeReference).target;
|
||||
let count = 0;
|
||||
for (let i = 0; i < depth; i++) {
|
||||
const t = stack[i];
|
||||
if (t.flags & TypeFlags.Object && t.symbol === symbol) {
|
||||
if (getObjectFlags(t) && ObjectFlags.Reference && !!(t as TypeReference).node && (t as TypeReference).target === root) {
|
||||
count++;
|
||||
if (count >= 5) return true;
|
||||
}
|
||||
@@ -19190,6 +19207,8 @@ namespace ts {
|
||||
let propagationType: Type;
|
||||
let inferencePriority = InferencePriority.MaxValue;
|
||||
let allowComplexConstraintInference = true;
|
||||
let objectTypeComparisonDepth = 0;
|
||||
const targetStack: Type[] = [];
|
||||
inferFromTypes(originalSource, originalTarget);
|
||||
|
||||
function inferFromTypes(source: Type, target: Type): void {
|
||||
@@ -19623,15 +19642,27 @@ 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 symbolOrType = isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined;
|
||||
const symbolOrType = getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node ? getNormalizedType(target, /*writing*/ false) : isNonConstructorObject ? isTupleType(target) ? target.target : target.symbol : undefined;
|
||||
if (symbolOrType) {
|
||||
if (contains(symbolOrTypeStack, symbolOrType)) {
|
||||
if (getObjectFlags(target) & ObjectFlags.Reference && (target as TypeReference).node) {
|
||||
// Don't set the circularity flag for re-encountered recursive type references just because we're already exploring them
|
||||
return;
|
||||
}
|
||||
inferencePriority = InferencePriority.Circularity;
|
||||
return;
|
||||
}
|
||||
targetStack[objectTypeComparisonDepth] = target;
|
||||
objectTypeComparisonDepth++;
|
||||
if (isDeeplyNestedType(target, targetStack, objectTypeComparisonDepth)) {
|
||||
inferencePriority = InferencePriority.Circularity;
|
||||
objectTypeComparisonDepth--;
|
||||
return;
|
||||
}
|
||||
(symbolOrTypeStack || (symbolOrTypeStack = [])).push(symbolOrType);
|
||||
inferFromObjectTypesWorker(source, target);
|
||||
symbolOrTypeStack.pop();
|
||||
objectTypeComparisonDepth--;
|
||||
}
|
||||
else {
|
||||
inferFromObjectTypesWorker(source, target);
|
||||
|
||||
Reference in New Issue
Block a user