Skip to content

Commit 2670b26

Browse files
committed
Move more logic to cached side of relation checks
1 parent 4866ce5 commit 2670b26

File tree

1 file changed

+30
-28
lines changed

1 file changed

+30
-28
lines changed

src/compiler/checker.ts

Lines changed: 30 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18361,31 +18361,6 @@ namespace ts {
1836118361
}
1836218362
}
1836318363

18364-
if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
18365-
// The combined constraint of an intersection type is the intersection of the constraints of
18366-
// the constituents. When an intersection type contains instantiable types with union type
18367-
// constraints, there are situations where we need to examine the combined constraint. One is
18368-
// when the target is a union type. Another is when the intersection contains types belonging
18369-
// to one of the disjoint domains. For example, given type variables T and U, each with the
18370-
// constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
18371-
// we need to check this constraint against a union on the target side. Also, given a type
18372-
// variable V constrained to 'string | number', 'V & number' has a combined constraint of
18373-
// 'string & number | number & number' which reduces to just 'number'.
18374-
// This also handles type parameters, as a type parameter with a union constraint compared against a union
18375-
// needs to have its constraint hoisted into an intersection with said type parameter, this way
18376-
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
18377-
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
18378-
const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
18379-
if (constraint && (source.flags & TypeFlags.Intersection || target.flags & TypeFlags.Union)) {
18380-
if (everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
18381-
// TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
18382-
if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
18383-
resetErrorInfo(saveErrorInfo);
18384-
}
18385-
}
18386-
}
18387-
}
18388-
1838918364
// For certain combinations involving intersections and optional, excess, or mismatched properties we need
1839018365
// an extra property check where the intersection is viewed as a single object. The following are motivating
1839118366
// examples that all should be errors, but aren't without this extra property check:
@@ -18970,17 +18945,44 @@ namespace ts {
1897018945
}
1897118946
}
1897218947
else if (sourceFlags & TypeFlags.UnionOrIntersection || targetFlags & TypeFlags.UnionOrIntersection) {
18973-
result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState);
18948+
if (result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState)) {
18949+
return result;
18950+
}
18951+
if (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union) {
18952+
// (T extends 1 | 2) & 1 <=> 1
18953+
// (T extends 1 | 2) <=> T & 1 | T & 2
18954+
// The combined constraint of an intersection type is the intersection of the constraints of
18955+
// the constituents. When an intersection type contains instantiable types with union type
18956+
// constraints, there are situations where we need to examine the combined constraint. One is
18957+
// when the target is a union type. Another is when the intersection contains types belonging
18958+
// to one of the disjoint domains. For example, given type variables T and U, each with the
18959+
// constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
18960+
// we need to check this constraint against a union on the target side. Also, given a type
18961+
// variable V constrained to 'string | number', 'V & number' has a combined constraint of
18962+
// 'string & number | number & number' which reduces to just 'number'.
18963+
// This also handles type parameters, as a type parameter with a union constraint compared against a union
18964+
// needs to have its constraint hoisted into an intersection with said type parameter, this way
18965+
// the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
18966+
// For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
18967+
const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
18968+
if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
18969+
// TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
18970+
if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
18971+
resetErrorInfo(saveErrorInfo);
18972+
return result;
18973+
}
18974+
}
18975+
}
1897418976
// The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
1897518977
// Source is instantiable (e.g. source has union or intersection constraint).
1897618978
// Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }).
1897718979
// Source is an intersection, target is an object (e.g. { a } & { b } <=> { a, b }).
1897818980
// Source is an intersection, target is a union (e.g. { a } & { b: boolean } <=> { a, b: true } | { a, b: false }).
1897918981
// Source is an intersection, target instantiable (e.g. string & { tag } <=> T["a"] constrained to string & { tag }).
18980-
if (result || !(sourceFlags & TypeFlags.Instantiable ||
18982+
if (!(sourceFlags & TypeFlags.Instantiable ||
1898118983
sourceFlags & TypeFlags.Object && targetFlags & TypeFlags.Union ||
1898218984
sourceFlags & TypeFlags.Intersection && targetFlags & (TypeFlags.Object | TypeFlags.Union | TypeFlags.Instantiable))) {
18983-
return result;
18985+
return Ternary.False;
1898418986
}
1898518987
}
1898618988

0 commit comments

Comments
 (0)