-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Optimize substitution types #50397
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Optimize substitution types #50397
Changes from all commits
c09b7dc
a4a9ede
e73d268
f48a923
95ea0ba
aed9659
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12344,7 +12344,7 @@ namespace ts { | |
return constraint && getBaseConstraint(constraint); | ||
} | ||
if (t.flags & TypeFlags.Substitution) { | ||
return getBaseConstraint((t as SubstitutionType).substitute); | ||
return getBaseConstraint(getSubstitutionIntersection(t as SubstitutionType)); | ||
} | ||
return t; | ||
} | ||
|
@@ -13881,22 +13881,27 @@ namespace ts { | |
return links.resolvedJSDocType; | ||
} | ||
|
||
function getSubstitutionType(baseType: Type, substitute: Type) { | ||
if (substitute.flags & TypeFlags.AnyOrUnknown || substitute === baseType) { | ||
function getSubstitutionType(baseType: Type, constraint: Type) { | ||
if (constraint.flags & TypeFlags.AnyOrUnknown || constraint === baseType || | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Is this just when someone writes T extends T ? TrueType<T> : FalseType<T> ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I presume so. The check was already there. |
||
!isGenericType(baseType) && !isGenericType(constraint)) { | ||
return baseType; | ||
} | ||
const id = `${getTypeId(baseType)}>${getTypeId(substitute)}`; | ||
const id = `${getTypeId(baseType)}>${getTypeId(constraint)}`; | ||
const cached = substitutionTypes.get(id); | ||
if (cached) { | ||
return cached; | ||
} | ||
const result = createType(TypeFlags.Substitution) as SubstitutionType; | ||
result.baseType = baseType; | ||
result.substitute = substitute; | ||
result.constraint = constraint; | ||
substitutionTypes.set(id, result); | ||
return result; | ||
} | ||
|
||
function getSubstitutionIntersection(substitutionType: SubstitutionType) { | ||
return getIntersectionType([substitutionType.constraint, substitutionType.baseType]); | ||
} | ||
|
||
function isUnaryTupleTypeNode(node: TypeNode) { | ||
return node.kind === SyntaxKind.TupleType && (node as TupleTypeNode).elements.length === 1; | ||
} | ||
|
@@ -13941,7 +13946,7 @@ namespace ts { | |
} | ||
node = parent; | ||
} | ||
return constraints ? getSubstitutionType(type, getIntersectionType(append(constraints, type))) : type; | ||
return constraints ? getSubstitutionType(type, getIntersectionType(constraints)) : type; | ||
} | ||
|
||
function isJSDocTypeReference(node: Node): node is TypeReferenceNode { | ||
|
@@ -15341,7 +15346,7 @@ namespace ts { | |
type.flags & TypeFlags.Conditional ? (type as ConditionalType).root.isDistributive && (type as ConditionalType).checkType === typeVariable : | ||
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) ? every((type as UnionOrIntersectionType | TemplateLiteralType).types, isDistributive) : | ||
type.flags & TypeFlags.IndexedAccess ? isDistributive((type as IndexedAccessType).objectType) && isDistributive((type as IndexedAccessType).indexType) : | ||
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).substitute) : | ||
type.flags & TypeFlags.Substitution ? isDistributive((type as SubstitutionType).baseType) && isDistributive((type as SubstitutionType).constraint): | ||
type.flags & TypeFlags.StringMapping ? isDistributive((type as StringMappingType).type) : | ||
false; | ||
} | ||
|
@@ -15861,7 +15866,7 @@ namespace ts { | |
if (type.flags & TypeFlags.Substitution) { | ||
if (!((type as SubstitutionType).objectFlags & ObjectFlags.IsGenericTypeComputed)) { | ||
(type as SubstitutionType).objectFlags |= ObjectFlags.IsGenericTypeComputed | | ||
getGenericObjectFlags((type as SubstitutionType).substitute) | getGenericObjectFlags((type as SubstitutionType).baseType); | ||
getGenericObjectFlags((type as SubstitutionType).baseType) | getGenericObjectFlags((type as SubstitutionType).constraint); | ||
} | ||
return (type as SubstitutionType).objectFlags & ObjectFlags.IsGenericType; | ||
} | ||
|
@@ -16073,11 +16078,7 @@ namespace ts { | |
const objectType = getTypeFromTypeNode(node.objectType); | ||
const indexType = getTypeFromTypeNode(node.indexType); | ||
const potentialAlias = getAliasSymbolForTypeNode(node); | ||
const resolved = getIndexedAccessType(objectType, indexType, AccessFlags.None, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias)); | ||
links.resolvedType = resolved.flags & TypeFlags.IndexedAccess && | ||
(resolved as IndexedAccessType).objectType === objectType && | ||
(resolved as IndexedAccessType).indexType === indexType ? | ||
getConditionalFlowTypeOfType(resolved, node) : resolved; | ||
links.resolvedType = getIndexedAccessType(objectType, indexType, AccessFlags.None, node, potentialAlias, getTypeArgumentsForAliasSymbol(potentialAlias)); | ||
} | ||
return links.resolvedType; | ||
} | ||
|
@@ -17018,9 +17019,9 @@ namespace ts { | |
} | ||
|
||
function getRestrictiveTypeParameter(tp: TypeParameter) { | ||
return tp.constraint === unknownType ? tp : tp.restrictiveInstantiation || ( | ||
return !tp.constraint && !getConstraintDeclaration(tp) || tp.constraint === noConstraintType ? tp : tp.restrictiveInstantiation || ( | ||
tp.restrictiveInstantiation = createTypeParameter(tp.symbol), | ||
(tp.restrictiveInstantiation as TypeParameter).constraint = unknownType, | ||
(tp.restrictiveInstantiation as TypeParameter).constraint = noConstraintType, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you mentioned it in person, but remind me again: Why does this need to be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The one difference I ran into is in Making matters worse, for a restrictive type parameter we used to jam in an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ew, icky. Distributive constraints behaving differently for |
||
tp.restrictiveInstantiation | ||
); | ||
} | ||
|
@@ -17407,17 +17408,18 @@ namespace ts { | |
return getConditionalTypeInstantiation(type as ConditionalType, combineTypeMappers((type as ConditionalType).mapper, mapper), aliasSymbol, aliasTypeArguments); | ||
} | ||
if (flags & TypeFlags.Substitution) { | ||
const maybeVariable = instantiateType((type as SubstitutionType).baseType, mapper); | ||
if (maybeVariable.flags & TypeFlags.TypeVariable) { | ||
return getSubstitutionType(maybeVariable as TypeVariable, instantiateType((type as SubstitutionType).substitute, mapper)); | ||
const newBaseType = instantiateType((type as SubstitutionType).baseType, mapper); | ||
const newConstraint = instantiateType((type as SubstitutionType).constraint, mapper); | ||
// A substitution type originates in the true branch of a conditional type and can be resolved | ||
// to just the base type in the same cases as the conditional type resolves to its true branch | ||
// (because the base type is then known to satisfy the constraint). | ||
if (newBaseType.flags & TypeFlags.TypeVariable && isGenericType(newConstraint)) { | ||
return getSubstitutionType(newBaseType, newConstraint); | ||
} | ||
else { | ||
const sub = instantiateType((type as SubstitutionType).substitute, mapper); | ||
if (sub.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(maybeVariable), getRestrictiveInstantiation(sub))) { | ||
return maybeVariable; | ||
} | ||
return sub; | ||
if (newConstraint.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(newBaseType), getRestrictiveInstantiation(newConstraint))) { | ||
return newBaseType; | ||
} | ||
return newBaseType.flags & TypeFlags.TypeVariable ? getSubstitutionType(newBaseType, newConstraint) : getIntersectionType([newConstraint, newBaseType]); | ||
} | ||
return type; | ||
} | ||
|
@@ -18456,7 +18458,7 @@ namespace ts { | |
const t = isFreshLiteralType(type) ? (type as FreshableType).regularType : | ||
getObjectFlags(type) & ObjectFlags.Reference ? (type as TypeReference).node ? createTypeReference((type as TypeReference).target, getTypeArguments(type as TypeReference)) : getSingleBaseForNonAugmentingSubtype(type) || type : | ||
type.flags & TypeFlags.UnionOrIntersection ? getNormalizedUnionOrIntersectionType(type as UnionOrIntersectionType, writing) : | ||
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : (type as SubstitutionType).substitute : | ||
type.flags & TypeFlags.Substitution ? writing ? (type as SubstitutionType).baseType : getSubstitutionIntersection(type as SubstitutionType) : | ||
type.flags & TypeFlags.Simplifiable ? getSimplifiedType(type, writing) : | ||
type; | ||
if (t === type) return t; | ||
|
@@ -19539,7 +19541,11 @@ namespace ts { | |
} | ||
} | ||
if (sourceFlags & TypeFlags.Substitution) { | ||
return isRelatedTo((source as SubstitutionType).substitute, (target as SubstitutionType).substitute, RecursionFlags.Both, /*reportErrors*/ false); | ||
if (result = isRelatedTo((source as SubstitutionType).baseType, (target as SubstitutionType).baseType, RecursionFlags.Both, /*reportErrors*/ false)) { | ||
if (result &= isRelatedTo((source as SubstitutionType).constraint, (target as SubstitutionType).constraint, RecursionFlags.Both, /*reportErrors*/ false)) { | ||
return result; | ||
} | ||
} | ||
} | ||
if (!(sourceFlags & TypeFlags.Object)) { | ||
return Ternary.False; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a meaningful break in behavior, right? Previously There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is in the code path for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's what would technically make it a break, right? Previously this related substitutions only, so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, that's true. But they definitely shouldn't be related, so I guess this fixes a bug as well. |
||
|
@@ -22677,7 +22683,7 @@ namespace ts { | |
} | ||
else if (source.flags & TypeFlags.Substitution) { | ||
inferFromTypes((source as SubstitutionType).baseType, target); | ||
inferWithPriority((source as SubstitutionType).substitute, target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority | ||
inferWithPriority(getSubstitutionIntersection(source as SubstitutionType), target, InferencePriority.SubstituteSource); // Make substitute inference at a lower priority | ||
} | ||
else if (target.flags & TypeFlags.Conditional) { | ||
invokeOnce(source, target, inferToConditionalType); | ||
|
Uh oh!
There was an error while loading. Please reload this page.