@@ -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