mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-10 18:04:18 -05:00
Cache results of expensive repetitive type operations (#49760)
This commit is contained in:
@@ -791,6 +791,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
const stringMappingTypes = new Map<string, StringMappingType>();
|
||||
const substitutionTypes = new Map<string, SubstitutionType>();
|
||||
const subtypeReductionCache = new Map<string, Type[]>();
|
||||
const cachedTypes = new Map<string, Type>();
|
||||
const evolvingArrayTypes: EvolvingArrayType[] = [];
|
||||
const undefinedProperties: SymbolTable = new Map();
|
||||
const markerTypes = new Set<number>();
|
||||
@@ -1090,6 +1091,15 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
|
||||
return checker;
|
||||
|
||||
function getCachedType(key: string | undefined) {
|
||||
return key ? cachedTypes.get(key) : undefined;
|
||||
}
|
||||
|
||||
function setCachedType(key: string | undefined, type: Type) {
|
||||
if (key) cachedTypes.set(key, type);
|
||||
return type;
|
||||
}
|
||||
|
||||
function getJsxNamespace(location: Node | undefined): __String {
|
||||
if (location) {
|
||||
const file = getSourceFileOfNode(location);
|
||||
@@ -21335,10 +21345,15 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
type.flags & TypeFlags.NumberLiteral ? numberType :
|
||||
type.flags & TypeFlags.BigIntLiteral ? bigintType :
|
||||
type.flags & TypeFlags.BooleanLiteral ? booleanType :
|
||||
type.flags & TypeFlags.Union ? mapType(type as UnionType, getBaseTypeOfLiteralType) :
|
||||
type.flags & TypeFlags.Union ? getBaseTypeOfLiteralTypeUnion(type as UnionType) :
|
||||
type;
|
||||
}
|
||||
|
||||
function getBaseTypeOfLiteralTypeUnion(type: UnionType) {
|
||||
const key = `B${getTypeId(type)}`;
|
||||
return getCachedType(key) ?? setCachedType(key, mapType(type, getBaseTypeOfLiteralType));
|
||||
}
|
||||
|
||||
function getWidenedLiteralType(type: Type): Type {
|
||||
return type.flags & TypeFlags.EnumLiteral && isFreshLiteralType(type) ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
|
||||
type.flags & TypeFlags.StringLiteral && isFreshLiteralType(type) ? stringType :
|
||||
@@ -23574,23 +23589,25 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
|
||||
// we remove type string.
|
||||
function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) {
|
||||
if (declaredType !== assignedType) {
|
||||
if (assignedType.flags & TypeFlags.Never) {
|
||||
return assignedType;
|
||||
}
|
||||
let reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
|
||||
if (assignedType.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(assignedType)) {
|
||||
reducedType = mapType(reducedType, getFreshTypeOfLiteralType); // Ensure that if the assignment is a fresh type, that we narrow to fresh types
|
||||
}
|
||||
// Our crude heuristic produces an invalid result in some cases: see GH#26130.
|
||||
// For now, when that happens, we give up and don't narrow at all. (This also
|
||||
// means we'll never narrow for erroneous assignments where the assigned type
|
||||
// is not assignable to the declared type.)
|
||||
if (isTypeAssignableTo(assignedType, reducedType)) {
|
||||
return reducedType;
|
||||
}
|
||||
if (declaredType === assignedType) {
|
||||
return declaredType;
|
||||
}
|
||||
return declaredType;
|
||||
if (assignedType.flags & TypeFlags.Never) {
|
||||
return assignedType;
|
||||
}
|
||||
const key = `A${getTypeId(declaredType)},${getTypeId(assignedType)}`;
|
||||
return getCachedType(key) ?? setCachedType(key, getAssignmentReducedTypeWorker(declaredType, assignedType));
|
||||
}
|
||||
|
||||
function getAssignmentReducedTypeWorker(declaredType: UnionType, assignedType: Type) {
|
||||
const filteredType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
|
||||
// Ensure that we narrow to fresh types if the assignment is a fresh boolean literal type.
|
||||
const reducedType = assignedType.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(assignedType) ? mapType(filteredType, getFreshTypeOfLiteralType) : filteredType;
|
||||
// Our crude heuristic produces an invalid result in some cases: see GH#26130.
|
||||
// For now, when that happens, we give up and don't narrow at all. (This also
|
||||
// means we'll never narrow for erroneous assignments where the assigned type
|
||||
// is not assignable to the declared type.)
|
||||
return isTypeAssignableTo(assignedType, reducedType) ? reducedType : declaredType;
|
||||
}
|
||||
|
||||
function isFunctionObjectType(type: ObjectType): boolean {
|
||||
@@ -25084,7 +25101,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
|
||||
? getTypeOfSymbol(classSymbol) as InterfaceType
|
||||
: getDeclaredTypeOfSymbol(classSymbol);
|
||||
return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
|
||||
return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
|
||||
}
|
||||
|
||||
function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
|
||||
@@ -25358,10 +25375,16 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
if (!nonConstructorTypeInUnion) return type;
|
||||
}
|
||||
|
||||
return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
|
||||
return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
|
||||
}
|
||||
|
||||
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
|
||||
function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
|
||||
const key = type.flags & TypeFlags.Union ? `N${getTypeId(type)},${getTypeId(candidate)},${(assumeTrue ? 1 : 0) | (checkDerived ? 2 : 0)}` : undefined;
|
||||
return getCachedType(key) ?? setCachedType(key, getNarrowedTypeWorker(type, candidate, assumeTrue, checkDerived));
|
||||
}
|
||||
|
||||
function getNarrowedTypeWorker(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
|
||||
const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
|
||||
if (!assumeTrue) {
|
||||
return filterType(type, t => !isRelated(t, candidate));
|
||||
}
|
||||
@@ -25373,7 +25396,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
return assignableType;
|
||||
}
|
||||
}
|
||||
|
||||
// If the candidate type is a subtype of the target type, narrow to the candidate type.
|
||||
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
|
||||
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
|
||||
@@ -25412,7 +25434,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
const predicateArgument = getTypePredicateArgument(predicate, callExpression);
|
||||
if (predicateArgument) {
|
||||
if (isMatchingReference(reference, predicateArgument)) {
|
||||
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
|
||||
return getNarrowedType(type, predicate.type, assumeTrue, /*checkDerived*/ false);
|
||||
}
|
||||
if (strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) &&
|
||||
!(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) {
|
||||
@@ -25420,7 +25442,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
|
||||
}
|
||||
const access = getDiscriminantPropertyAccess(predicateArgument, type);
|
||||
if (access) {
|
||||
return narrowTypeByDiscriminant(type, access, t => getNarrowedType(t, predicate.type!, assumeTrue, isTypeSubtypeOf));
|
||||
return narrowTypeByDiscriminant(type, access, t => getNarrowedType(t, predicate.type!, assumeTrue, /*checkDerived*/ false));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user