Relate non-augmenting subtypes without resorting to structural comparison (#43624)

* Relate non-augmenting array subtypes without resorting to structural comparison

* Fix lint

* Generalize performance enhancement

* Cache results, feed through via getNormalizedType to remove error intermediates

* Use newly freed up object flags to limit member setting, fix crash with those object flags

* Move flags because there is no TypeFlags.Reference 🤦
This commit is contained in:
Wesley Wigham
2021-04-27 22:52:12 -07:00
committed by GitHub
parent 5e4fcfbfb6
commit 7748694d60
15 changed files with 68 additions and 49 deletions

View File

@@ -17232,12 +17232,13 @@ namespace ts {
function getNormalizedType(type: Type, writing: boolean): Type {
while (true) {
const t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
let t = isFreshLiteralType(type) ? (<FreshableType>type).regularType :
getObjectFlags(type) & ObjectFlags.Reference && (<TypeReference>type).node ? createTypeReference((<TypeReference>type).target, getTypeArguments(<TypeReference>type)) :
type.flags & TypeFlags.UnionOrIntersection ? getReducedType(type) :
type.flags & TypeFlags.Substitution ? writing ? (<SubstitutionType>type).baseType : (<SubstitutionType>type).substitute :
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
type;
t = getSingleBaseForNonAugmentingSubtype(t) || t;
if (t === type) break;
type = t;
}
@@ -17739,8 +17740,10 @@ namespace ts {
function reportErrorResults(source: Type, target: Type, result: Ternary, isComparingJsxAttributes: boolean) {
if (!result && reportErrors) {
source = originalSource.aliasSymbol ? originalSource : source;
target = originalTarget.aliasSymbol ? originalTarget : target;
const sourceHasBase = !!getSingleBaseForNonAugmentingSubtype(originalSource);
const targetHasBase = !!getSingleBaseForNonAugmentingSubtype(originalTarget);
source = (originalSource.aliasSymbol || sourceHasBase) ? originalSource : source;
target = (originalTarget.aliasSymbol || targetHasBase) ? originalTarget : target;
let maybeSuppress = overrideNextErrorInfo > 0;
if (maybeSuppress) {
overrideNextErrorInfo--;
@@ -19993,6 +19996,30 @@ namespace ts {
return isArrayType(type) || !(type.flags & TypeFlags.Nullable) && isTypeAssignableTo(type, anyReadonlyArrayType);
}
function getSingleBaseForNonAugmentingSubtype(type: Type) {
if (!(getObjectFlags(type) & ObjectFlags.Reference) || !(getObjectFlags((type as TypeReference).target) & ObjectFlags.ClassOrInterface)) {
return undefined;
}
if (getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeCalculated) {
return getObjectFlags(type) & ObjectFlags.IdenticalBaseTypeExists ? (type as TypeReference).cachedEquivalentBaseType : undefined;
}
(type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeCalculated;
const target = (type as TypeReference).target as InterfaceType;
const bases = getBaseTypes(target);
if (bases.length !== 1) {
return undefined;
}
if (getMembersOfSymbol(type.symbol).size) {
return undefined; // If the interface has any members, they may subtype members in the base, so we should do a full structural comparison
}
let instantiatedBase = !length(target.typeParameters) ? bases[0] : instantiateType(bases[0], createTypeMapper(target.typeParameters!, getTypeArguments(type as TypeReference).slice(0, target.typeParameters!.length)));
if (length(getTypeArguments(type as TypeReference)) > length(target.typeParameters)) {
instantiatedBase = getTypeWithThisArgument(instantiatedBase, last(getTypeArguments(type as TypeReference)));
}
(type as TypeReference).objectFlags |= ObjectFlags.IdenticalBaseTypeExists;
return (type as TypeReference).cachedEquivalentBaseType = instantiatedBase;
}
function isEmptyArrayLiteralType(type: Type): boolean {
const elementType = getElementTypeOfArrayType(type);
return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType;

View File

@@ -5200,6 +5200,11 @@ namespace ts {
ObjectRestType = 1 << 23, // Originates in object rest declaration
/* @internal */
IsClassInstanceClone = 1 << 24, // Type is a clone of a class instance type
// Flags that require TypeFlags.Object and ObjectFlags.Reference
/* @internal */
IdenticalBaseTypeCalculated = 1 << 25, // has had `getSingleBaseForNonAugmentingSubtype` invoked on it already
/* @internal */
IdenticalBaseTypeExists = 1 << 26, // has a defined cachedEquivalentBaseType member
// Flags that require TypeFlags.UnionOrIntersection or TypeFlags.Substitution
/* @internal */
@@ -5281,6 +5286,8 @@ namespace ts {
resolvedTypeArguments?: readonly Type[]; // Resolved type reference type arguments
/* @internal */
literalType?: TypeReference; // Clone of type with ObjectFlags.ArrayLiteral set
/* @internal */
cachedEquivalentBaseType?: Type; // Only set on references to class or interfaces with a single base type and no augmentations
}
export interface DeferredTypeReference extends TypeReference {