Skip to content

Commit c09b7dc

Browse files
committed
Optimize substitution type infrastructure
1 parent 76357ba commit c09b7dc

File tree

3 files changed

+35
-25
lines changed

3 files changed

+35
-25
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12344,7 +12344,7 @@ namespace ts {
1234412344
return constraint && getBaseConstraint(constraint);
1234512345
}
1234612346
if (t.flags & TypeFlags.Substitution) {
12347-
return getBaseConstraint((t as SubstitutionType).substitute);
12347+
return getBaseConstraint(getSubstitutionIntersection(t as SubstitutionType));
1234812348
}
1234912349
return t;
1235012350
}
@@ -13881,22 +13881,28 @@ namespace ts {
1388113881
return links.resolvedJSDocType;
1388213882
}
1388313883

13884-
function getSubstitutionType(baseType: Type, substitute: Type) {
13885-
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) {
13884+
function getSubstitutionType(baseType: Type, constraint: Type) {
13885+
if (constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType ||
13886+
baseType.flags & TypeFlags.Substitution && (baseType as SubstitutionType).constraint === constraint ||
13887+
!isGenericType(baseType) && !isGenericType(constraint)) {
1388613888
return baseType;
1388713889
}
13888-
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`;
13890+
const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`;
1388913891
const cached = substitutionTypes.get(id);
1389013892
if (cached) {
1389113893
return cached;
1389213894
}
1389313895
const result = createType(TypeFlags.Substitution) as SubstitutionType;
1389413896
result.baseType = baseType;
13895-
result.substitute = substitute;
13897+
result.constraint = constraint;
1389613898
substitutionTypes.set(id, result);
1389713899
return result;
1389813900
}
1389913901

13902+
function getSubstitutionIntersection(substitutionType: SubstitutionType) {
13903+
return getIntersectionType([substitutionType.constraint, substitutionType.baseType]);
13904+
}
13905+
1390013906
function isUnaryTupleTypeNode(node: TypeNode) {
1390113907
return node.kind === SyntaxKind.TupleType && (node as TupleTypeNode).elements.length === 1;
1390213908
}
@@ -13941,7 +13947,7 @@ namespace ts {
1394113947
}
1394213948
node = parent;
1394313949
}
13944-
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type;
13950+
return constraints ? getSubstitutionType(type, getIntersectionType(constraints)) : type;
1394513951
}
1394613952

1394713953
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
@@ -15341,7 +15347,7 @@ namespace ts {
1534115347
type.flags & TypeFlags.Conditional ? (type as ConditionalType).root.isDistributive && (type as ConditionalType).checkType === typeVariable :
1534215348
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) ? every((type as UnionOrIntersectionType | TemplateLiteralType).types, isDistributive) :
1534315349
type.flags & TypeFlags.IndexedAccess ? isDistributive((type as IndexedAccessType).objectType) && isDistributive((type as IndexedAccessType).indexType) :
15344-
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).substitute) :
15350+
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).baseType) && isDistributive((type as SubstitutionType).constraint):
1534515351
type.flags & TypeFlags.StringMapping ? isDistributive((type as StringMappingType).type) :
1534615352
false;
1534715353
}
@@ -15861,7 +15867,7 @@ namespace ts {
1586115867
if (type.flags & TypeFlags.Substitution) {
1586215868
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
1586315869
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
15864-
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType);
15870+
getGenericObjectFlags((type as SubstitutionType).baseType) | getGenericObjectFlags((type as SubstitutionType).constraint);
1586515871
}
1586615872
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
1586715873
}
@@ -17407,17 +17413,17 @@ namespace ts {
1740717413
return getConditionalTypeInstantiation(type as ConditionalType, combineTypeMappers((type as ConditionalType).mapper, mapper), aliasSymbol, aliasTypeArguments);
1740817414
}
1740917415
if (flags & TypeFlags.Substitution) {
17410-
const maybeVariable = instantiateType((type as SubstitutionType).baseType, mapper);
17411-
if (maybeVariable.flags & TypeFlags.TypeVariable) {
17412-
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((type as SubstitutionType).substitute, mapper));
17413-
}
17414-
else {
17415-
const sub = instantiateType((type as SubstitutionType).substitute, mapper);
17416-
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
17417-
return maybeVariable;
17418-
}
17419-
return sub;
17420-
}
17416+
const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper);
17417+
const newConstraint = instantiateType((type as SubstitutionType).constraint, mapper);
17418+
// A substitution type originates in the true branch of a conditional type and can be resolved
17419+
// to just the base type in the same cases as the conditional type resolves to its true branch
17420+
// (because the base type is then known to satisfy the constraint).
17421+
if (newConstraint.flags & TypeFlags.AnyOrUnknown ||
17422+
!isGenericType(newBaseType) && !isGenericType(newConstraint) ||
17423+
isTypeAssignableTo(getRestrictiveInstantiation(newBaseType), getRestrictiveInstantiation(newConstraint))) {
17424+
return newBaseType;
17425+
}
17426+
return getSubstitutionType(newBaseType, newConstraint);
1742117427
}
1742217428
return type;
1742317429
}
@@ -18456,7 +18462,7 @@ namespace ts {
1845618462
const t = isFreshLiteralType(type) ? (type as FreshableType).regularType :
1845718463
getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : getSingleBaseForNonAugmentingSubtype(type) || type :
1845818464
type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) :
18459-
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : (type as SubstitutionType).substitute :
18465+
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : getSubstitutionIntersection(type as SubstitutionType) :
1846018466
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
1846118467
type;
1846218468
if (t === type) return t;
@@ -19539,7 +19545,11 @@ namespace ts {
1953919545
}
1954019546
}
1954119547
if (sourceFlags & TypeFlags.Substitution) {
19542-
return isRelatedTo((source as SubstitutionType).substitute, (target as SubstitutionType).substitute, RecursionFlags.Both, /*reportErrors*/ false);
19548+
if (result = isRelatedTo((source as SubstitutionType).baseType, (target as SubstitutionType).baseType, RecursionFlags.Both, /*reportErrors*/ false)) {
19549+
if (result &= isRelatedTo((source as SubstitutionType).constraint, (target as SubstitutionType).constraint, RecursionFlags.Both, /*reportErrors*/ false)) {
19550+
return result;
19551+
}
19552+
}
1954319553
}
1954419554
if (!(sourceFlags & TypeFlags.Object)) {
1954519555
return Ternary.False;
@@ -22677,7 +22687,7 @@ namespace ts {
2267722687
}
2267822688
else if (source.flags & TypeFlags.Substitution) {
2267922689
inferFromTypes((source as SubstitutionType).baseType, target);
22680-
inferWithPriority((source as SubstitutionType).substitute, target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority
22690+
inferWithPriority(getSubstitutionIntersection(source as SubstitutionType), target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority
2268122691
}
2268222692
else if (target.flags & TypeFlags.Conditional) {
2268322693
invokeOnce(source, target, inferToConditionalType);

src/compiler/tracing.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ namespace ts { // eslint-disable-line one-namespace-per-file
253253
const substitutionType = type as SubstitutionType;
254254
substitutionProperties = {
255255
substitutionBaseType: substitutionType.baseType?.id,
256-
substituteType: substitutionType.substitute?.id,
256+
constraintType: substitutionType.constraint?.id,
257257
};
258258
}
259259

src/compiler/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6077,8 +6077,8 @@ namespace ts {
60776077
// types disappear upon instantiation (just like type parameters).
60786078
export interface SubstitutionType extends InstantiableType {
60796079
objectFlags: ObjectFlags;
6080-
baseType: Type; // Target type
6081-
substitute: Type; // Type to substitute for type parameter
6080+
baseType: Type; // Target type
6081+
constraint: Type; // Constraint that target type is known to satisfy
60826082
}
60836083

60846084
/* @internal */

0 commit comments

Comments
 (0)