@@ -791,6 +791,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
791
791
const stringMappingTypes = new Map<string, StringMappingType>();
792
792
const substitutionTypes = new Map<string, SubstitutionType>();
793
793
const subtypeReductionCache = new Map<string, Type[]>();
794
+ const cachedTypes = new Map<string, Type>();
794
795
const evolvingArrayTypes: EvolvingArrayType[] = [];
795
796
const undefinedProperties: SymbolTable = new Map();
796
797
const markerTypes = new Set<number>();
@@ -1090,6 +1091,15 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
1090
1091
1091
1092
return checker;
1092
1093
1094
+ function getCachedType(key: string | undefined) {
1095
+ return key ? cachedTypes.get(key) : undefined;
1096
+ }
1097
+
1098
+ function setCachedType(key: string | undefined, type: Type) {
1099
+ if (key) cachedTypes.set(key, type);
1100
+ return type;
1101
+ }
1102
+
1093
1103
function getJsxNamespace(location: Node | undefined): __String {
1094
1104
if (location) {
1095
1105
const file = getSourceFileOfNode(location);
@@ -21335,10 +21345,15 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
21335
21345
type.flags & TypeFlags.NumberLiteral ? numberType :
21336
21346
type.flags & TypeFlags.BigIntLiteral ? bigintType :
21337
21347
type.flags & TypeFlags.BooleanLiteral ? booleanType :
21338
- type.flags & TypeFlags.Union ? mapType (type as UnionType, getBaseTypeOfLiteralType ) :
21348
+ type.flags & TypeFlags.Union ? getBaseTypeOfLiteralTypeUnion (type as UnionType) :
21339
21349
type;
21340
21350
}
21341
21351
21352
+ function getBaseTypeOfLiteralTypeUnion(type: UnionType) {
21353
+ const key = `B${getTypeId(type)}`;
21354
+ return getCachedType(key) ?? setCachedType(key, mapType(type, getBaseTypeOfLiteralType));
21355
+ }
21356
+
21342
21357
function getWidenedLiteralType(type: Type): Type {
21343
21358
return type.flags & TypeFlags.EnumLiteral && isFreshLiteralType(type) ? getBaseTypeOfEnumLiteralType(type as LiteralType) :
21344
21359
type.flags & TypeFlags.StringLiteral && isFreshLiteralType(type) ? stringType :
@@ -23574,23 +23589,25 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
23574
23589
// For example, when a variable of type number | string | boolean is assigned a value of type number | boolean,
23575
23590
// we remove type string.
23576
23591
function getAssignmentReducedType(declaredType: UnionType, assignedType: Type) {
23577
- if (declaredType !== assignedType) {
23578
- if (assignedType.flags & TypeFlags.Never) {
23579
- return assignedType;
23580
- }
23581
- let reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
23582
- if (assignedType.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(assignedType)) {
23583
- reducedType = mapType(reducedType, getFreshTypeOfLiteralType); // Ensure that if the assignment is a fresh type, that we narrow to fresh types
23584
- }
23585
- // Our crude heuristic produces an invalid result in some cases: see GH#26130.
23586
- // For now, when that happens, we give up and don't narrow at all. (This also
23587
- // means we'll never narrow for erroneous assignments where the assigned type
23588
- // is not assignable to the declared type.)
23589
- if (isTypeAssignableTo(assignedType, reducedType)) {
23590
- return reducedType;
23591
- }
23592
+ if (declaredType === assignedType) {
23593
+ return declaredType;
23592
23594
}
23593
- return declaredType;
23595
+ if (assignedType.flags & TypeFlags.Never) {
23596
+ return assignedType;
23597
+ }
23598
+ const key = `A${getTypeId(declaredType)},${getTypeId(assignedType)}`;
23599
+ return getCachedType(key) ?? setCachedType(key, getAssignmentReducedTypeWorker(declaredType, assignedType));
23600
+ }
23601
+
23602
+ function getAssignmentReducedTypeWorker(declaredType: UnionType, assignedType: Type) {
23603
+ const filteredType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t));
23604
+ // Ensure that we narrow to fresh types if the assignment is a fresh boolean literal type.
23605
+ const reducedType = assignedType.flags & TypeFlags.BooleanLiteral && isFreshLiteralType(assignedType) ? mapType(filteredType, getFreshTypeOfLiteralType) : filteredType;
23606
+ // Our crude heuristic produces an invalid result in some cases: see GH#26130.
23607
+ // For now, when that happens, we give up and don't narrow at all. (This also
23608
+ // means we'll never narrow for erroneous assignments where the assigned type
23609
+ // is not assignable to the declared type.)
23610
+ return isTypeAssignableTo(assignedType, reducedType) ? reducedType : declaredType;
23594
23611
}
23595
23612
23596
23613
function isFunctionObjectType(type: ObjectType): boolean {
@@ -25084,7 +25101,7 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
25084
25101
const targetType = hasStaticModifier(Debug.checkDefined(symbol.valueDeclaration, "should always have a declaration"))
25085
25102
? getTypeOfSymbol(classSymbol) as InterfaceType
25086
25103
: getDeclaredTypeOfSymbol(classSymbol);
25087
- return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom );
25104
+ return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true );
25088
25105
}
25089
25106
25090
25107
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")
25358
25375
if (!nonConstructorTypeInUnion) return type;
25359
25376
}
25360
25377
25361
- return getNarrowedType(type, targetType, assumeTrue, isTypeDerivedFrom);
25378
+ return getNarrowedType(type, targetType, assumeTrue, /*checkDerived*/ true);
25379
+ }
25380
+
25381
+ function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
25382
+ const key = type.flags & TypeFlags.Union ? `N${getTypeId(type)},${getTypeId(candidate)},${(assumeTrue ? 1 : 0) | (checkDerived ? 2 : 0)}` : undefined;
25383
+ return getCachedType(key) ?? setCachedType(key, getNarrowedTypeWorker(type, candidate, assumeTrue, checkDerived));
25362
25384
}
25363
25385
25364
- function getNarrowedType(type: Type, candidate: Type, assumeTrue: boolean, isRelated: (source: Type, target: Type) => boolean) {
25386
+ function getNarrowedTypeWorker(type: Type, candidate: Type, assumeTrue: boolean, checkDerived: boolean) {
25387
+ const isRelated = checkDerived ? isTypeDerivedFrom : isTypeSubtypeOf;
25365
25388
if (!assumeTrue) {
25366
25389
return filterType(type, t => !isRelated(t, candidate));
25367
25390
}
@@ -25373,7 +25396,6 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
25373
25396
return assignableType;
25374
25397
}
25375
25398
}
25376
-
25377
25399
// If the candidate type is a subtype of the target type, narrow to the candidate type.
25378
25400
// Otherwise, if the target type is assignable to the candidate type, keep the target type.
25379
25401
// Otherwise, if the candidate type is assignable to the target type, narrow to the candidate
@@ -25412,15 +25434,15 @@ m2: ${(this.mapper2 as unknown as DebugTypeMapper).__debugToString().split("\n")
25412
25434
const predicateArgument = getTypePredicateArgument(predicate, callExpression);
25413
25435
if (predicateArgument) {
25414
25436
if (isMatchingReference(reference, predicateArgument)) {
25415
- return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf );
25437
+ return getNarrowedType(type, predicate.type, assumeTrue, /*checkDerived*/ false );
25416
25438
}
25417
25439
if (strictNullChecks && assumeTrue && optionalChainContainsReference(predicateArgument, reference) &&
25418
25440
!(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) {
25419
25441
type = getAdjustedTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
25420
25442
}
25421
25443
const access = getDiscriminantPropertyAccess(predicateArgument, type);
25422
25444
if (access) {
25423
- return narrowTypeByDiscriminant(type, access, t => getNarrowedType(t, predicate.type!, assumeTrue, isTypeSubtypeOf ));
25445
+ return narrowTypeByDiscriminant(type, access, t => getNarrowedType(t, predicate.type!, assumeTrue, /*checkDerived*/ false ));
25424
25446
}
25425
25447
}
25426
25448
}
0 commit comments