Cache simplified indexed accesses to better handle circularly constrained indexed acceses (#24072)

This commit is contained in:
Wesley Wigham 2018-05-14 12:54:26 -07:00 committed by GitHub
parent 7e3af08a09
commit ba4bf21ead
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 53 additions and 5 deletions

View File

@ -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) {

View File

@ -3973,6 +3973,7 @@ namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
}
export type TypeVariable = TypeParameter | IndexedAccessType;

View File

@ -2248,6 +2248,7 @@ declare namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
}
type TypeVariable = TypeParameter | IndexedAccessType;
interface IndexType extends InstantiableType {

View File

@ -2248,6 +2248,7 @@ declare namespace ts {
objectType: Type;
indexType: Type;
constraint?: Type;
simplified?: Type;
}
type TypeVariable = TypeParameter | IndexedAccessType;
interface IndexType extends InstantiableType {

View File

@ -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]

View File

@ -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))
};

View File

@ -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
};

View File

@ -0,0 +1,3 @@
type Loop<T, U extends Loop<T, U>> = {
[P in keyof T]: U[P] extends boolean ? number : string;
};