Skip to content

Commit 47f26e6

Browse files
committed
Use strictSubtypeRelation for union subtype reduction
1 parent 6d67054 commit 47f26e6

File tree

1 file changed

+10
-23
lines changed

1 file changed

+10
-23
lines changed

src/compiler/checker.ts

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,7 @@ namespace ts {
871871
let outofbandVarianceMarkerHandler: ((onlyUnreliable: boolean) => void) | undefined;
872872

873873
const subtypeRelation = createMap<RelationComparisonResult>();
874+
const strictSubtypeRelation = createMap<RelationComparisonResult>();
874875
const assignableRelation = createMap<RelationComparisonResult>();
875876
const comparableRelation = createMap<RelationComparisonResult>();
876877
const identityRelation = createMap<RelationComparisonResult>();
@@ -11398,7 +11399,7 @@ namespace ts {
1139811399
}
1139911400
}
1140011401
count++;
11401-
if (isTypeSubtypeOf(source, target) && (
11402+
if (isTypeRelatedTo(source, target, strictSubtypeRelation) && (
1140211403
!(getObjectFlags(getTargetType(source)) & ObjectFlags.Class) ||
1140311404
!(getObjectFlags(getTargetType(target)) & ObjectFlags.Class) ||
1140411405
isTypeDerivedFrom(source, target))) {
@@ -14301,7 +14302,7 @@ namespace ts {
1430114302
return true;
1430214303
}
1430314304
if (source.flags & TypeFlags.Object && target.flags & TypeFlags.Object) {
14304-
const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, /*strictArityChecks*/ false, relation));
14305+
const related = relation.get(getRelationKey(source, target, /*isIntersectionConstituent*/ false, relation));
1430514306
if (related !== undefined) {
1430614307
return !!(related & RelationComparisonResult.Succeeded);
1430714308
}
@@ -14353,7 +14354,6 @@ namespace ts {
1435314354
let depth = 0;
1435414355
let expandingFlags = ExpandingFlags.None;
1435514356
let overflow = false;
14356-
let strictArityChecks = relation === subtypeRelation;
1435714357
let overrideNextErrorInfo = 0; // How many `reportRelationError` calls should be skipped in the elaboration pyramid
1435814358
let lastSkippedInfo: [Type, Type] | undefined;
1435914359
let incompatibleStack: [DiagnosticMessage, (string | number)?, (string | number)?, (string | number)?, (string | number)?][] = [];
@@ -15142,7 +15142,7 @@ namespace ts {
1514215142
if (overflow) {
1514315143
return Ternary.False;
1514415144
}
15145-
const id = getRelationKey(source, target, isIntersectionConstituent, strictArityChecks, relation);
15145+
const id = getRelationKey(source, target, isIntersectionConstituent, relation);
1514615146
const entry = relation.get(id);
1514715147
if (entry !== undefined) {
1514815148
if (reportErrors && entry & RelationComparisonResult.Failed && !(entry & RelationComparisonResult.Reported)) {
@@ -15438,7 +15438,7 @@ namespace ts {
1543815438
}
1543915439
else {
1544015440
// An empty object type is related to any mapped type that includes a '?' modifier.
15441-
if (relation !== subtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
15441+
if (relation !== subtypeRelation && relation !== strictSubtypeRelation && isPartialMappedType(target) && isEmptyObjectType(source)) {
1544215442
return Ternary.True;
1544315443
}
1544415444
if (isGenericMappedType(target)) {
@@ -15480,7 +15480,7 @@ namespace ts {
1548015480
}
1548115481
// Consider a fresh empty object literal type "closed" under the subtype relationship - this way `{} <- {[idx: string]: any} <- fresh({})`
1548215482
// and not `{} <- fresh({}) <- {[idx: string]: any}`
15483-
else if (relation === subtypeRelation && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
15483+
else if ((relation === subtypeRelation || relation === strictSubtypeRelation) && isEmptyObjectType(target) && getObjectFlags(target) & ObjectFlags.FreshLiteral && !isEmptyObjectType(source)) {
1548415484
return Ternary.False;
1548515485
}
1548615486
// Even if relationship doesn't hold for unions, intersections, or generic type references,
@@ -15489,18 +15489,6 @@ namespace ts {
1548915489
// to X. Failing both of those we want to check if the aggregation of A and B's members structurally
1549015490
// relates to X. Thus, we include intersection types on the source side here.
1549115491
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection) && target.flags & TypeFlags.Object) {
15492-
// When performing strict arity checks for signatures (under the subtype relationship), if source has
15493-
// every property of target, and target is missing some properties from source, then we disable strict
15494-
// arity checks for the members. This decreases the chances of "ties" in union subtype reduction. For
15495-
// example, it enables us to consider { f(): void } a supertype of { f(x?: string): void, g(): void },
15496-
// which we need for backwards compatibility reasons (specifically, for Object and Number).
15497-
const saveStrictArityChecks = strictArityChecks;
15498-
if (strictArityChecks &&
15499-
!getUnmatchedProperty(source, target, /*requireOptionalProperties*/ true, /*matchDiscriminantProperties*/ false) &&
15500-
getUnmatchedProperty(target, source, /*requireOptionalProperties*/ true, /*matchDiscriminantProperties*/ false)) {
15501-
strictArityChecks = false;
15502-
}
15503-
// Report structural errors only if we haven't reported any errors yet
1550415492
const reportStructuralErrors = reportErrors && errorInfo === saveErrorInfo.errorInfo && !sourceIsPrimitive;
1550515493
result = propertiesRelatedTo(source, target, reportStructuralErrors, /*excludedProperties*/ undefined, isIntersectionConstituent);
1550615494
if (result) {
@@ -15515,7 +15503,6 @@ namespace ts {
1551515503
}
1551615504
}
1551715505
}
15518-
strictArityChecks = saveStrictArityChecks;
1551915506
if (varianceCheckFailed && result) {
1552015507
errorInfo = originalErrorInfo || errorInfo || saveErrorInfo.errorInfo; // Use variance error (there is no structural one) and return false
1552115508
}
@@ -15837,7 +15824,7 @@ namespace ts {
1583715824
if (relation === identityRelation) {
1583815825
return propertiesIdenticalTo(source, target, excludedProperties);
1583915826
}
15840-
const requireOptionalProperties = relation === subtypeRelation && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
15827+
const requireOptionalProperties = (relation === subtypeRelation || relation === strictSubtypeRelation) && !isObjectLiteralType(source) && !isEmptyArrayLiteralType(source) && !isTupleType(source);
1584115828
const unmatchedProperty = getUnmatchedProperty(source, target, requireOptionalProperties, /*matchDiscriminantProperties*/ false);
1584215829
if (unmatchedProperty) {
1584315830
if (reportErrors) {
@@ -16060,7 +16047,7 @@ namespace ts {
1606016047
*/
1606116048
function signatureRelatedTo(source: Signature, target: Signature, erase: boolean, reportErrors: boolean, incompatibleReporter: (source: Type, target: Type) => void): Ternary {
1606216049
return compareSignaturesRelated(erase ? getErasedSignature(source) : source, erase ? getErasedSignature(target) : target,
16063-
strictArityChecks ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
16050+
relation === strictSubtypeRelation ? SignatureCheckMode.StrictArity : 0, reportErrors, reportError, incompatibleReporter, isRelatedTo, reportUnreliableMarkers);
1606416051
}
1606516052

1606616053
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {
@@ -16368,13 +16355,13 @@ namespace ts {
1636816355
* To improve caching, the relation key for two generic types uses the target's id plus ids of the type parameters.
1636916356
* For other cases, the types ids are used.
1637016357
*/
16371-
function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, strictArityChecks: boolean, relation: Map<RelationComparisonResult>) {
16358+
function getRelationKey(source: Type, target: Type, isIntersectionConstituent: boolean, relation: Map<RelationComparisonResult>) {
1637216359
if (relation === identityRelation && source.id > target.id) {
1637316360
const temp = source;
1637416361
source = target;
1637516362
target = temp;
1637616363
}
16377-
const delimiter = isIntersectionConstituent ? strictArityChecks ? "|" : ";" : strictArityChecks ? "/" : ",";
16364+
const delimiter = isIntersectionConstituent ? ";" : ",";
1637816365
if (isTypeReferenceWithGenericArguments(source) && isTypeReferenceWithGenericArguments(target)) {
1637916366
const typeParameters: Type[] = [];
1638016367
return getTypeReferenceId(<TypeReference>source, typeParameters) + delimiter + getTypeReferenceId(<TypeReference>target, typeParameters);

0 commit comments

Comments
 (0)