Skip to content

Commit 0ced684

Browse files
committed
Fox equality narrowing and comparable relation for intersections with {}
1 parent a70bb9d commit 0ced684

File tree

1 file changed

+18
-22
lines changed

1 file changed

+18
-22
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19191,11 +19191,15 @@ namespace ts {
1919119191
// parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
1919219192
// appear to be comparable to '2'.
1919319193
if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
19194-
const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
19194+
const constraints = sameMap((source as IntersectionType).types, t => t.flags & TypeFlags.Instantiable ? getBaseConstraintOfType(t) || unknownType : t);
1919519195
if (constraints !== (source as IntersectionType).types) {
1919619196
source = getIntersectionType(constraints);
19197+
if (source.flags & TypeFlags.Never) {
19198+
return Ternary.False;
19199+
}
1919719200
if (!(source.flags & TypeFlags.Intersection)) {
19198-
return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false);
19201+
return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false) ||
19202+
isRelatedTo(target, source, RecursionFlags.Source, /*reportErrors*/ false);
1919919203
}
1920019204
}
1920119205
}
@@ -19516,7 +19520,7 @@ namespace ts {
1951619520
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
1951719521
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
1951819522
const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
19519-
if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
19523+
if (constraint && !(constraint.flags & TypeFlags.Never) && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
1952019524
// TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
1952119525
result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
1952219526
}
@@ -25294,25 +25298,11 @@ namespace ts {
2529425298
assumeTrue = !assumeTrue;
2529525299
}
2529625300
const valueType = getTypeOfExpression(value);
25297-
if (((type.flags & TypeFlags.Unknown) || isEmptyAnonymousObjectType(type) && !(valueType.flags & TypeFlags.Nullable)) &&
25298-
assumeTrue &&
25299-
(operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)
25300-
) {
25301-
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive)) {
25302-
return valueType;
25303-
}
25304-
if (valueType.flags & TypeFlags.Object) {
25305-
return nonPrimitiveType;
25306-
}
25307-
if (type.flags & TypeFlags.Unknown) {
25308-
return type;
25309-
}
25310-
}
25301+
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
2531125302
if (valueType.flags & TypeFlags.Nullable) {
2531225303
if (!strictNullChecks) {
2531325304
return type;
2531425305
}
25315-
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
2531625306
const facts = doubleEquals ?
2531725307
assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull :
2531825308
valueType.flags & TypeFlags.Null ?
@@ -25321,10 +25311,16 @@ namespace ts {
2532125311
return getAdjustedTypeWithFacts(type, facts);
2532225312
}
2532325313
if (assumeTrue) {
25324-
const filterFn: (t: Type) => boolean = operator === SyntaxKind.EqualsEqualsToken ?
25325-
t => areTypesComparable(t, valueType) || isCoercibleUnderDoubleEquals(t, valueType) :
25326-
t => areTypesComparable(t, valueType);
25327-
return replacePrimitivesWithLiterals(filterType(type, filterFn), valueType);
25314+
if (!doubleEquals && (type.flags & TypeFlags.Unknown || someType(type, isEmptyAnonymousObjectType))) {
25315+
if (valueType.flags & (TypeFlags.Primitive | TypeFlags.NonPrimitive) || isEmptyAnonymousObjectType(valueType)) {
25316+
return valueType;
25317+
}
25318+
if (valueType.flags & TypeFlags.Object) {
25319+
return nonPrimitiveType;
25320+
}
25321+
}
25322+
const filteredType = filterType(type, t => areTypesComparable(t, valueType) || doubleEquals && isCoercibleUnderDoubleEquals(t, valueType));
25323+
return replacePrimitivesWithLiterals(filteredType, valueType);
2532825324
}
2532925325
if (isUnitType(valueType)) {
2533025326
return filterType(type, t => !(isUnitLikeType(t) && areTypesComparable(t, valueType)));

0 commit comments

Comments
 (0)