Skip to content

Commit 08c0fa9

Browse files
committed
Limit structural traversal to only types containing marker types
1 parent a323037 commit 08c0fa9

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

src/compiler/checker.ts

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19059,6 +19059,9 @@ namespace ts {
1905919059
return varianceResult;
1906019060
}
1906119061
}
19062+
else if (!someContainsMarkerType(source.aliasTypeArguments) && !someContainsMarkerType(target.aliasTypeArguments)) {
19063+
return Ternary.Unknown;
19064+
}
1906219065
}
1906319066

1906419067
// For a generic type T and a type U that is assignable to T, [...U] is assignable to T, U is assignable to readonly [...T],
@@ -19436,14 +19439,18 @@ namespace ts {
1943619439
}
1943719440
// We have type references to the same generic type. Obtain the variance information for the type
1943819441
// parameters and relate the type arguments accordingly. If variance information for the type is in
19439-
// the process of being computed, fall through and relate the type references structurally.
19442+
// the process of being computed, structurally relate references that contain marker types in their
19443+
// type arguments, but otherwise return Ternary.Unknown (the non-cached version of Ternary.Maybe).
1944019444
const variances = getVariances((source as TypeReference).target);
1944119445
if (variances !== emptyArray) {
1944219446
const varianceResult = relateVariances(getTypeArguments(source as TypeReference), getTypeArguments(target as TypeReference), variances, intersectionState);
1944319447
if (varianceResult !== undefined) {
1944419448
return varianceResult;
1944519449
}
1944619450
}
19451+
else if (!someContainsMarkerType(getTypeArguments(source as TypeReference)) && !someContainsMarkerType(getTypeArguments(target as TypeReference))) {
19452+
return Ternary.Unknown;
19453+
}
1944719454
}
1944819455
else if (isReadonlyArrayType(target) ? isArrayType(source) || isTupleType(source) : isArrayType(target) && isTupleType(source) && !source.target.readonly) {
1944919456
if (relation !== identityRelation) {
@@ -20427,6 +20434,49 @@ namespace ts {
2042720434
return createTypeReference(type, instantiateTypes(type.typeParameters, mapper));
2042820435
}
2042920436

20437+
// Check if any of the given types contain marker types. We visit type arguments of generic classes, interfaces, and
20438+
// type aliases, members of anonymous type literals and mapped types, constituents of union, intersection, and
20439+
// template literal types, and targets of index types and string mapping types. This is by design a limited set of
20440+
// types to avoid excessive fan out. In deferred (and thus possibly circular) locations we limit the traversal to
20441+
// four levels of depth.
20442+
function someContainsMarkerType(types: readonly Type[] | undefined, maxDepth = 4) {
20443+
return some(types, t => containsMarkerType(t, maxDepth));
20444+
}
20445+
20446+
function containsMarkerType(type: Type, maxDepth: number): boolean {
20447+
return maxDepth > 0 && (
20448+
type.flags & TypeFlags.TypeParameter ? type === markerSubType || type === markerSubType || type === markerOtherType :
20449+
type.aliasSymbol ? someContainsMarkerType(type.aliasTypeArguments, maxDepth) :
20450+
type.flags & TypeFlags.Object ? objectTypeContainsMarkerType(type as ObjectType, maxDepth) :
20451+
type.flags & (TypeFlags.UnionOrIntersection | TypeFlags.TemplateLiteral) && !(type.flags & TypeFlags.EnumLiteral) ? someContainsMarkerType((type as UnionOrIntersectionType | TemplateLiteralType).types, maxDepth) :
20452+
type.flags & (TypeFlags.Index | TypeFlags.StringMapping) ? containsMarkerType((type as IndexType | StringMappingType).type, maxDepth) :
20453+
false);
20454+
}
20455+
20456+
function objectTypeContainsMarkerType(type: ObjectType, maxDepth: number): boolean {
20457+
const objectFlags = getObjectFlags(type);
20458+
if (objectFlags & ObjectFlags.Reference) {
20459+
return someContainsMarkerType(getTypeArguments(type as TypeReference), maxDepth - ((type as TypeReference).node ? 1 : 0));
20460+
}
20461+
if (objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.TypeLiteral) {
20462+
const resolved = resolveStructuredTypeMembers(type);
20463+
return some(resolved.properties, prop => containsMarkerType(getTypeOfSymbol(prop), maxDepth - 1)) ||
20464+
some(resolved.callSignatures, sig => signatureContainsMarkerType(sig, maxDepth)) ||
20465+
some(resolved.constructSignatures, sig => signatureContainsMarkerType(sig, maxDepth)) ||
20466+
some(resolved.indexInfos, info => containsMarkerType(info.type, maxDepth - 1));
20467+
}
20468+
if (objectFlags & ObjectFlags.Mapped) {
20469+
return containsMarkerType(getConstraintTypeFromMappedType(type as MappedType), maxDepth - 1) ||
20470+
containsMarkerType(getTemplateTypeFromMappedType(type as MappedType), maxDepth - 1);
20471+
}
20472+
return false;
20473+
}
20474+
20475+
function signatureContainsMarkerType(sig: Signature, maxDepth: number): boolean {
20476+
return some(sig.parameters, param => containsMarkerType(getTypeOfSymbol(param), maxDepth - 1)) ||
20477+
containsMarkerType(getReturnTypeOfSignature(sig), maxDepth - 1);
20478+
}
20479+
2043020480
// Return true if the given type reference has a 'void' type argument for a covariant type parameter.
2043120481
// See comment at call in recursiveTypeRelatedTo for when this case matters.
2043220482
function hasCovariantVoidArgument(typeArguments: readonly Type[], variances: VarianceFlags[]): boolean {

0 commit comments

Comments
 (0)