mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-04 21:53:42 -06:00
Cache simplified indexed accesses to better handle circularly constrained indexed acceses (#24072)
This commit is contained in:
parent
7e3af08a09
commit
ba4bf21ead
@ -8692,6 +8692,10 @@ namespace ts {
|
||||
// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
|
||||
// the type itself if no transformation is possible.
|
||||
function getSimplifiedIndexedAccessType(type: IndexedAccessType): Type {
|
||||
if (type.simplified) {
|
||||
return type.simplified === circularConstraintType ? type : type.simplified;
|
||||
}
|
||||
type.simplified = circularConstraintType;
|
||||
const objectType = type.objectType;
|
||||
if (objectType.flags & TypeFlags.Intersection && isGenericObjectType(objectType)) {
|
||||
// Given an indexed access type T[K], if T is an intersection containing one or more generic types and one or
|
||||
@ -8709,7 +8713,7 @@ namespace ts {
|
||||
regularTypes.push(t);
|
||||
}
|
||||
}
|
||||
return getUnionType([
|
||||
return type.simplified = getUnionType([
|
||||
getSimplifiedType(getIndexedAccessType(getIntersectionType(regularTypes), type.indexType)),
|
||||
getIntersectionType(stringIndexTypes)
|
||||
]);
|
||||
@ -8720,7 +8724,7 @@ namespace ts {
|
||||
// eventually anyway, but it easier to reason about.
|
||||
if (some((<IntersectionType>objectType).types, isMappedTypeToNever)) {
|
||||
const nonNeverTypes = filter((<IntersectionType>objectType).types, t => !isMappedTypeToNever(t));
|
||||
return getSimplifiedType(getIndexedAccessType(getIntersectionType(nonNeverTypes), type.indexType));
|
||||
return type.simplified = getSimplifiedType(getIndexedAccessType(getIntersectionType(nonNeverTypes), type.indexType));
|
||||
}
|
||||
}
|
||||
// If the object type is a mapped type { [P in K]: E }, where K is generic, instantiate E using a mapper
|
||||
@ -8728,15 +8732,15 @@ namespace ts {
|
||||
// construct the type Box<T[X]>. We do not further simplify the result because mapped types can be recursive
|
||||
// and we might never terminate.
|
||||
if (isGenericMappedType(objectType)) {
|
||||
return substituteIndexedMappedType(objectType, type);
|
||||
return type.simplified = substituteIndexedMappedType(objectType, type);
|
||||
}
|
||||
if (objectType.flags & TypeFlags.TypeParameter) {
|
||||
const constraint = getConstraintFromTypeParameter(objectType as TypeParameter);
|
||||
if (constraint && isGenericMappedType(constraint)) {
|
||||
return substituteIndexedMappedType(constraint, type);
|
||||
return type.simplified = substituteIndexedMappedType(constraint, type);
|
||||
}
|
||||
}
|
||||
return type;
|
||||
return type.simplified = type;
|
||||
}
|
||||
|
||||
function substituteIndexedMappedType(objectType: MappedType, type: IndexedAccessType) {
|
||||
|
||||
@ -3973,6 +3973,7 @@ namespace ts {
|
||||
objectType: Type;
|
||||
indexType: Type;
|
||||
constraint?: Type;
|
||||
simplified?: Type;
|
||||
}
|
||||
|
||||
export type TypeVariable = TypeParameter | IndexedAccessType;
|
||||
|
||||
@ -2248,6 +2248,7 @@ declare namespace ts {
|
||||
objectType: Type;
|
||||
indexType: Type;
|
||||
constraint?: Type;
|
||||
simplified?: Type;
|
||||
}
|
||||
type TypeVariable = TypeParameter | IndexedAccessType;
|
||||
interface IndexType extends InstantiableType {
|
||||
|
||||
@ -2248,6 +2248,7 @@ declare namespace ts {
|
||||
objectType: Type;
|
||||
indexType: Type;
|
||||
constraint?: Type;
|
||||
simplified?: Type;
|
||||
}
|
||||
type TypeVariable = TypeParameter | IndexedAccessType;
|
||||
interface IndexType extends InstantiableType {
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
//// [circularConstrainedMappedTypeNoCrash.ts]
|
||||
type Loop<T, U extends Loop<T, U>> = {
|
||||
[P in keyof T]: U[P] extends boolean ? number : string;
|
||||
};
|
||||
|
||||
//// [circularConstrainedMappedTypeNoCrash.js]
|
||||
@ -0,0 +1,16 @@
|
||||
=== tests/cases/compiler/circularConstrainedMappedTypeNoCrash.ts ===
|
||||
type Loop<T, U extends Loop<T, U>> = {
|
||||
>Loop : Symbol(Loop, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 10))
|
||||
>U : Symbol(U, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 12))
|
||||
>Loop : Symbol(Loop, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 0))
|
||||
>T : Symbol(T, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 10))
|
||||
>U : Symbol(U, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 12))
|
||||
|
||||
[P in keyof T]: U[P] extends boolean ? number : string;
|
||||
>P : Symbol(P, Decl(circularConstrainedMappedTypeNoCrash.ts, 1, 5))
|
||||
>T : Symbol(T, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 10))
|
||||
>U : Symbol(U, Decl(circularConstrainedMappedTypeNoCrash.ts, 0, 12))
|
||||
>P : Symbol(P, Decl(circularConstrainedMappedTypeNoCrash.ts, 1, 5))
|
||||
|
||||
};
|
||||
@ -0,0 +1,16 @@
|
||||
=== tests/cases/compiler/circularConstrainedMappedTypeNoCrash.ts ===
|
||||
type Loop<T, U extends Loop<T, U>> = {
|
||||
>Loop : Loop<T, U>
|
||||
>T : T
|
||||
>U : U
|
||||
>Loop : Loop<T, U>
|
||||
>T : T
|
||||
>U : U
|
||||
|
||||
[P in keyof T]: U[P] extends boolean ? number : string;
|
||||
>P : P
|
||||
>T : T
|
||||
>U : U
|
||||
>P : P
|
||||
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
type Loop<T, U extends Loop<T, U>> = {
|
||||
[P in keyof T]: U[P] extends boolean ? number : string;
|
||||
};
|
||||
Loading…
x
Reference in New Issue
Block a user