Skip to content

Commit 6e8337e

Browse files
authoredAug 26, 2022
Optimize substitution types (#50397)
* Optimize substitution type infrastructure * Accept new baselines * Preserve instantiated substitution types for type variables * Restrictive type parameters should have no constraint * Fix issues from top100 test run * Accept new baselines
1 parent 226dd0b commit 6e8337e

File tree

5 files changed

+38
-32
lines changed

5 files changed

+38
-32
lines changed
 

‎src/compiler/checker.ts

+33-27
Original file line numberDiff line numberDiff line change
@@ -12366,7 +12366,7 @@ namespace ts {
1236612366
return constraint && getBaseConstraint(constraint);
1236712367
}
1236812368
if (t.flags & TypeFlags.Substitution) {
12369-
return getBaseConstraint((t as SubstitutionType).substitute);
12369+
return getBaseConstraint(getSubstitutionIntersection(t as SubstitutionType));
1237012370
}
1237112371
return t;
1237212372
}
@@ -13903,22 +13903,27 @@ namespace ts {
1390313903
return links.resolvedJSDocType;
1390413904
}
1390513905

13906-
function getSubstitutionType(baseType: Type, substitute: Type) {
13907-
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) {
13906+
function getSubstitutionType(baseType: Type, constraint: Type) {
13907+
if (constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType ||
13908+
!isGenericType(baseType) && !isGenericType(constraint)) {
1390813909
return baseType;
1390913910
}
13910-
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`;
13911+
const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`;
1391113912
const cached = substitutionTypes.get(id);
1391213913
if (cached) {
1391313914
return cached;
1391413915
}
1391513916
const result = createType(TypeFlags.Substitution) as SubstitutionType;
1391613917
result.baseType = baseType;
13917-
result.substitute = substitute;
13918+
result.constraint = constraint;
1391813919
substitutionTypes.set(id, result);
1391913920
return result;
1392013921
}
1392113922

13923+
function getSubstitutionIntersection(substitutionType: SubstitutionType) {
13924+
return getIntersectionType([substitutionType.constraint, substitutionType.baseType]);
13925+
}
13926+
1392213927
function isUnaryTupleTypeNode(node: TypeNode) {
1392313928
return node.kind === SyntaxKind.TupleType && (node as TupleTypeNode).elements.length === 1;
1392413929
}
@@ -13963,7 +13968,7 @@ namespace ts {
1396313968
}
1396413969
node = parent;
1396513970
}
13966-
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type;
13971+
return constraints ? getSubstitutionType(type, getIntersectionType(constraints)) : type;
1396713972
}
1396813973

1396913974
function isJSDocTypeReference(node: Node): node is TypeReferenceNode {
@@ -15363,7 +15368,7 @@ namespace ts {
1536315368
type.flags & TypeFlags.Conditional ? (type as ConditionalType).root.isDistributive && (type as ConditionalType).checkType === typeVariable :
1536415369
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) ? every((type as UnionOrIntersectionType | TemplateLiteralType).types, isDistributive) :
1536515370
type.flags & TypeFlags.IndexedAccess ? isDistributive((type as IndexedAccessType).objectType) && isDistributive((type as IndexedAccessType).indexType) :
15366-
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).substitute) :
15371+
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).baseType) && isDistributive((type as SubstitutionType).constraint):
1536715372
type.flags & TypeFlags.StringMapping ? isDistributive((type as StringMappingType).type) :
1536815373
false;
1536915374
}
@@ -15883,7 +15888,7 @@ namespace ts {
1588315888
if (type.flags & TypeFlags.Substitution) {
1588415889
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) {
1588515890
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed |
15886-
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType);
15891+
getGenericObjectFlags((type as SubstitutionType).baseType) | getGenericObjectFlags((type as SubstitutionType).constraint);
1588715892
}
1588815893
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType;
1588915894
}
@@ -16095,11 +16100,7 @@ namespace ts {
1609516100
const objectType = getTypeFromTypeNode(node.objectType);
1609616101
const indexType = getTypeFromTypeNode(node.indexType);
1609716102
const potentialAlias = getAliasSymbolForTypeNode(node);
16098-
const resolved = getIndexedAccessType(objectType, indexType, AccessFlags.None, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias));
16099-
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess &&
16100-
(resolved as IndexedAccessType).objectType === objectType &&
16101-
(resolved as IndexedAccessType).indexType === indexType ?
16102-
getConditionalFlowTypeOfType(resolved, node) : resolved;
16103+
links.resolvedType = getIndexedAccessType(objectType, indexType, AccessFlags.None, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias));
1610316104
}
1610416105
return links.resolvedType;
1610516106
}
@@ -17040,9 +17041,9 @@ namespace ts {
1704017041
}
1704117042

1704217043
function getRestrictiveTypeParameter(tp: TypeParameter) {
17043-
return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || (
17044+
return !tp.constraint && !getConstraintDeclaration(tp) || tp.constraint === noConstraintType ? tp : tp.restrictiveInstantiation || (
1704417045
tp.restrictiveInstantiation = createTypeParameter(tp.symbol),
17045-
(tp.restrictiveInstantiation as TypeParameter).constraint = unknownType,
17046+
(tp.restrictiveInstantiation as TypeParameter).constraint = noConstraintType,
1704617047
tp.restrictiveInstantiation
1704717048
);
1704817049
}
@@ -17429,17 +17430,18 @@ namespace ts {
1742917430
return getConditionalTypeInstantiation(type as ConditionalType, combineTypeMappers((type as ConditionalType).mapper, mapper), aliasSymbol, aliasTypeArguments);
1743017431
}
1743117432
if (flags & TypeFlags.Substitution) {
17432-
const maybeVariable = instantiateType((type as SubstitutionType).baseType, mapper);
17433-
if (maybeVariable.flags & TypeFlags.TypeVariable) {
17434-
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((type as SubstitutionType).substitute, mapper));
17433+
const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper);
17434+
const newConstraint = instantiateType((type as SubstitutionType).constraint, mapper);
17435+
// A substitution type originates in the true branch of a conditional type and can be resolved
17436+
// to just the base type in the same cases as the conditional type resolves to its true branch
17437+
// (because the base type is then known to satisfy the constraint).
17438+
if (newBaseType.flags & TypeFlags.TypeVariable && isGenericType(newConstraint)) {
17439+
return getSubstitutionType(newBaseType, newConstraint);
1743517440
}
17436-
else {
17437-
const sub = instantiateType((type as SubstitutionType).substitute, mapper);
17438-
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) {
17439-
return maybeVariable;
17440-
}
17441-
return sub;
17441+
if (newConstraint.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(newBaseType), getRestrictiveInstantiation(newConstraint))) {
17442+
return newBaseType;
1744217443
}
17444+
return newBaseType.flags & TypeFlags.TypeVariable ? getSubstitutionType(newBaseType, newConstraint) : getIntersectionType([newConstraint, newBaseType]);
1744317445
}
1744417446
return type;
1744517447
}
@@ -18478,7 +18480,7 @@ namespace ts {
1847818480
const t = isFreshLiteralType(type) ? (type as FreshableType).regularType :
1847918481
getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : getSingleBaseForNonAugmentingSubtype(type) || type :
1848018482
type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) :
18481-
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : (type as SubstitutionType).substitute :
18483+
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : getSubstitutionIntersection(type as SubstitutionType) :
1848218484
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) :
1848318485
type;
1848418486
if (t === type) return t;
@@ -19561,7 +19563,11 @@ namespace ts {
1956119563
}
1956219564
}
1956319565
if (sourceFlags & TypeFlags.Substitution) {
19564-
return isRelatedTo((source as SubstitutionType).substitute, (target as SubstitutionType).substitute, RecursionFlags.Both, /*reportErrors*/ false);
19566+
if (result = isRelatedTo((source as SubstitutionType).baseType, (target as SubstitutionType).baseType, RecursionFlags.Both, /*reportErrors*/ false)) {
19567+
if (result &= isRelatedTo((source as SubstitutionType).constraint, (target as SubstitutionType).constraint, RecursionFlags.Both, /*reportErrors*/ false)) {
19568+
return result;
19569+
}
19570+
}
1956519571
}
1956619572
if (!(sourceFlags & TypeFlags.Object)) {
1956719573
return Ternary.False;
@@ -22699,7 +22705,7 @@ namespace ts {
2269922705
}
2270022706
else if (source.flags & TypeFlags.Substitution) {
2270122707
inferFromTypes((source as SubstitutionType).baseType, target);
22702-
inferWithPriority((source as SubstitutionType).substitute, target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority
22708+
inferWithPriority(getSubstitutionIntersection(source as SubstitutionType), target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority
2270322709
}
2270422710
else if (target.flags & TypeFlags.Conditional) {
2270522711
invokeOnce(source, target, inferToConditionalType);

‎src/compiler/tracing.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ namespace ts { // eslint-disable-line local/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

+2-2
Original file line numberDiff line numberDiff line change
@@ -6145,8 +6145,8 @@ namespace ts {
61456145
// types disappear upon instantiation (just like type parameters).
61466146
export interface SubstitutionType extends InstantiableType {
61476147
objectFlags: ObjectFlags;
6148-
baseType: Type; // Target type
6149-
substitute: Type; // Type to substitute for type parameter
6148+
baseType: Type; // Target type
6149+
constraint: Type; // Constraint that target type is known to satisfy
61506150
}
61516151

61526152
/* @internal */

‎tests/baselines/reference/api/tsserverlibrary.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2840,7 +2840,7 @@ declare namespace ts {
28402840
export interface SubstitutionType extends InstantiableType {
28412841
objectFlags: ObjectFlags;
28422842
baseType: Type;
2843-
substitute: Type;
2843+
constraint: Type;
28442844
}
28452845
export enum SignatureKind {
28462846
Call = 0,

‎tests/baselines/reference/api/typescript.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2840,7 +2840,7 @@ declare namespace ts {
28402840
export interface SubstitutionType extends InstantiableType {
28412841
objectFlags: ObjectFlags;
28422842
baseType: Type;
2843-
substitute: Type;
2843+
constraint: Type;
28442844
}
28452845
export enum SignatureKind {
28462846
Call = 0,

0 commit comments

Comments
 (0)
Please sign in to comment.