@@ -19383,7 +19383,27 @@ namespace ts {
19383
19383
19384
19384
function structuredTypeRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
19385
19385
const saveErrorInfo = captureErrorCalculationState();
19386
- const result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState, saveErrorInfo);
19386
+ let result = structuredTypeRelatedToWorker(source, target, reportErrors, intersectionState, saveErrorInfo);
19387
+ if (!result && (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union)) {
19388
+ // The combined constraint of an intersection type is the intersection of the constraints of
19389
+ // the constituents. When an intersection type contains instantiable types with union type
19390
+ // constraints, there are situations where we need to examine the combined constraint. One is
19391
+ // when the target is a union type. Another is when the intersection contains types belonging
19392
+ // to one of the disjoint domains. For example, given type variables T and U, each with the
19393
+ // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
19394
+ // we need to check this constraint against a union on the target side. Also, given a type
19395
+ // variable V constrained to 'string | number', 'V & number' has a combined constraint of
19396
+ // 'string & number | number & number' which reduces to just 'number'.
19397
+ // This also handles type parameters, as a type parameter with a union constraint compared against a union
19398
+ // needs to have its constraint hoisted into an intersection with said type parameter, this way
19399
+ // the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
19400
+ // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
19401
+ const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
19402
+ if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
19403
+ // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
19404
+ result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
19405
+ }
19406
+ }
19387
19407
if (result) {
19388
19408
resetErrorInfo(saveErrorInfo);
19389
19409
}
@@ -19442,28 +19462,6 @@ namespace ts {
19442
19462
if (result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState)) {
19443
19463
return result;
19444
19464
}
19445
- if (source.flags & TypeFlags.Intersection || source.flags & TypeFlags.TypeParameter && target.flags & TypeFlags.Union) {
19446
- // The combined constraint of an intersection type is the intersection of the constraints of
19447
- // the constituents. When an intersection type contains instantiable types with union type
19448
- // constraints, there are situations where we need to examine the combined constraint. One is
19449
- // when the target is a union type. Another is when the intersection contains types belonging
19450
- // to one of the disjoint domains. For example, given type variables T and U, each with the
19451
- // constraint 'string | number', the combined constraint of 'T & U' is 'string | number' and
19452
- // we need to check this constraint against a union on the target side. Also, given a type
19453
- // variable V constrained to 'string | number', 'V & number' has a combined constraint of
19454
- // 'string & number | number & number' which reduces to just 'number'.
19455
- // This also handles type parameters, as a type parameter with a union constraint compared against a union
19456
- // needs to have its constraint hoisted into an intersection with said type parameter, this way
19457
- // the type param can be compared with itself in the target (with the influence of its constraint to match other parts)
19458
- // For example, if `T extends 1 | 2` and `U extends 2 | 3` and we compare `T & U` to `T & U & (1 | 2 | 3)`
19459
- const constraint = getEffectiveConstraintOfIntersection(source.flags & TypeFlags.Intersection ? (source as IntersectionType).types: [source], !!(target.flags & TypeFlags.Union));
19460
- if (constraint && everyType(constraint, c => c !== source)) { // Skip comparison if expansion contains the source itself
19461
- // TODO: Stack errors so we get a pyramid for the "normal" comparison above, _and_ a second for this
19462
- if (result = isRelatedTo(constraint, target, RecursionFlags.Source, /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState)) {
19463
- return result;
19464
- }
19465
- }
19466
- }
19467
19465
// The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
19468
19466
// Source is instantiable (e.g. source has union or intersection constraint).
19469
19467
// Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }).
0 commit comments