Skip to content

Commit f19959a

Browse files
committed
Cache substitution types and remove erasure that was too eager
1 parent fc7d1c3 commit f19959a

1 file changed

Lines changed: 22 additions & 19 deletions

File tree

src/compiler/checker.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ namespace ts {
303303
const literalTypes = createMap<LiteralType>();
304304
const indexedAccessTypes = createMap<IndexedAccessType>();
305305
const conditionalTypes = createMap<ConditionalType>();
306+
const substitutionTypes = createMap<SubstitutionType>();
306307
const evolvingArrayTypes: EvolvingArrayType[] = [];
307308
const undefinedProperties = createMap<Symbol>() as UnderscoreEscapedMap<Symbol>;
308309

@@ -6964,15 +6965,7 @@ namespace ts {
69646965
return result & TypeFlags.PropagatingFlags;
69656966
}
69666967

6967-
// This function replaces substitution types with their underlying type parameters. We erase when creating
6968-
// type references and type alias instantiations because substitution types are no longer necessary once
6969-
// the type arguments have been validated against their corresponding type parameter constraints.
6970-
function eraseSubstitutionType(type: Type) {
6971-
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeParameter : type;
6972-
}
6973-
69746968
function createTypeReference(target: GenericType, typeArguments: Type[]): TypeReference {
6975-
typeArguments = sameMap(typeArguments, eraseSubstitutionType);
69766969
const id = getTypeListId(typeArguments);
69776970
let type = target.instantiations.get(id);
69786971
if (!type) {
@@ -7039,7 +7032,6 @@ namespace ts {
70397032
}
70407033

70417034
function getTypeAliasInstantiation(symbol: Symbol, typeArguments: Type[]): Type {
7042-
typeArguments = sameMap(typeArguments, eraseSubstitutionType);
70437035
const type = getDeclaredTypeOfSymbol(symbol);
70447036
const links = getSymbolLinks(symbol);
70457037
const typeParameters = links.typeParameters;
@@ -7163,6 +7155,19 @@ namespace ts {
71637155
}
71647156
}
71657157

7158+
function getSubstitutionType(typeParameter: TypeParameter, substitute: Type) {
7159+
const id = typeParameter.id + "," + substitute.id;
7160+
const cached = substitutionTypes.get(id);
7161+
if (cached) {
7162+
return cached;
7163+
}
7164+
const result = <SubstitutionType>createType(TypeFlags.Substitution);
7165+
result.typeParameter = typeParameter;
7166+
result.substitute = substitute;
7167+
substitutionTypes.set(id, result);
7168+
return result;
7169+
}
7170+
71667171
function getConstrainedTypeParameter(typeParameter: TypeParameter, node: Node) {
71677172
let constraints: Type[];
71687173
while (isTypeNode(node)) {
@@ -7174,13 +7179,7 @@ namespace ts {
71747179
}
71757180
node = parent;
71767181
}
7177-
if (constraints) {
7178-
const result = <SubstitutionType>createType(TypeFlags.Substitution);
7179-
result.typeParameter = typeParameter;
7180-
result.substitute = getIntersectionType(append(constraints, typeParameter));
7181-
return result;
7182-
}
7183-
return typeParameter;
7182+
return constraints ? getSubstitutionType(typeParameter, getIntersectionType(append(constraints, typeParameter))) : typeParameter;
71847183
}
71857184

71867185
function isJSDocTypeReference(node: TypeReferenceType): node is TypeReferenceNode {
@@ -8082,6 +8081,10 @@ namespace ts {
80828081
return links.resolvedType;
80838082
}
80848083

8084+
function getActualTypeParameter(type: Type) {
8085+
return type.flags & TypeFlags.Substitution ? (<SubstitutionType>type).typeParameter : type;
8086+
}
8087+
80858088
function createConditionalType(checkType: Type, extendsType: Type, trueType: Type, falseType: Type, target: ConditionalType, mapper: TypeMapper, aliasSymbol: Symbol, aliasTypeArguments: Type[]) {
80868089
const type = <ConditionalType>createType(TypeFlags.Conditional);
80878090
type.checkType = checkType;
@@ -8113,7 +8116,7 @@ namespace ts {
81138116
return falseType;
81148117
}
81158118
// Return a deferred type for a check that is neither definitely true nor definitely false
8116-
return createConditionalType(eraseSubstitutionType(checkType), extendsType, trueType, falseType,
8119+
return createConditionalType(getActualTypeParameter(checkType), extendsType, trueType, falseType,
81178120
/*target*/ undefined, /*mapper*/ undefined, aliasSymbol, aliasTypeArguments);
81188121
}
81198122

@@ -8724,7 +8727,7 @@ namespace ts {
87248727
return instantiateType(type.falseType, mapper);
87258728
}
87268729
// Return a deferred type for a check that is neither definitely true nor definitely false
8727-
const erasedCheckType = eraseSubstitutionType(checkType);
8730+
const erasedCheckType = getActualTypeParameter(checkType);
87288731
const trueType = instantiateType(type.trueType, mapper);
87298732
const falseType = instantiateType(type.falseType, mapper);
87308733
const id = type.id + "," + erasedCheckType.id + "," + extendsType.id + "," + trueType.id + "," + falseType.id;
@@ -8773,7 +8776,7 @@ namespace ts {
87738776
return getConditionalTypeInstantiation(<ConditionalType>type, mapper);
87748777
}
87758778
if (type.flags & TypeFlags.Substitution) {
8776-
return instantiateType((<SubstitutionType>type).typeParameter, mapper);
8779+
return mapper((<SubstitutionType>type).typeParameter);
87778780
}
87788781
}
87798782
return type;

0 commit comments

Comments
 (0)