@@ -18346,30 +18346,17 @@ namespace ts {
18346
18346
let result = Ternary.False;
18347
18347
const saveErrorInfo = captureErrorCalculationState();
18348
18348
18349
- if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
18350
- // We skip caching when source or target is a union with no more than three constituents.
18351
- result = (source.flags & TypeFlags.Union || target.flags & TypeFlags.Union) && getConstituentCount(source) * getConstituentCount(target) < 4 ?
18352
- structuredTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck) :
18353
- recursiveTypeRelatedTo(source, target, reportErrors, intersectionState | IntersectionState.UnionIntersectionCheck, recursionFlags);
18354
- // The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
18355
- // Source is instantiable (e.g. source has union or intersection constraint).
18356
- // Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }).
18357
- // Source is an intersection, target is an object (e.g. { a } & { b } <=> { a, b }).
18358
- // Source is an intersection, target is a union (e.g. { a } & { b: boolean } <=> { a, b: true } | { a, b: false }).
18359
- // Source is an intersection, target instantiable (e.g. string & { tag } <=> T["a"] constrained to string & { tag }).
18360
- if (!result && (source.flags & TypeFlags.Instantiable ||
18361
- source.flags & TypeFlags.Object && target.flags & TypeFlags.Union ||
18362
- source.flags & TypeFlags.Intersection && target.flags & (TypeFlags.Object | TypeFlags.Union | TypeFlags.Instantiable))) {
18363
- if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
18364
- resetErrorInfo(saveErrorInfo);
18365
- }
18349
+ if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
18350
+ const skipCaching = source.flags & TypeFlags.Union && (source as UnionType).types.length < 4 && !(target.flags & TypeFlags.Union) ||
18351
+ target.flags & TypeFlags.Union && (target as UnionType).types.length < 4 && !(source.flags & TypeFlags.StructuredOrInstantiable);
18352
+ if (skipCaching) {
18353
+ result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState);
18366
18354
}
18367
- }
18368
- else if (source.flags & TypeFlags.StructuredOrInstantiable || target.flags & TypeFlags.StructuredOrInstantiable) {
18369
- if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
18355
+ else if (result = recursiveTypeRelatedTo(source, target, reportErrors, intersectionState, recursionFlags)) {
18370
18356
resetErrorInfo(saveErrorInfo);
18371
18357
}
18372
18358
}
18359
+
18373
18360
if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
18374
18361
// The combined constraint of an intersection type is the intersection of the constraints of
18375
18362
// the constituents. When an intersection type contains instantiable types with union type
@@ -18606,6 +18593,51 @@ namespace ts {
18606
18593
return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent === container.valueDeclaration;
18607
18594
}
18608
18595
18596
+ function unionOrIntersectionRelatedTo(source: Type, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
18597
+ // Note that these checks are specifically ordered to produce correct results. In particular,
18598
+ // we need to deconstruct unions before intersections (because unions are always at the top),
18599
+ // and we need to handle "each" relations before "some" relations for the same kind of type.
18600
+ if (source.flags & TypeFlags.Union) {
18601
+ return relation === comparableRelation ?
18602
+ someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck) :
18603
+ eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck);
18604
+ }
18605
+ if (target.flags & TypeFlags.Union) {
18606
+ return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
18607
+ }
18608
+ if (target.flags & TypeFlags.Intersection) {
18609
+ return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
18610
+ }
18611
+ // Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
18612
+ // constraints of all non-primitive types in the source into a new intersection. We do this because the
18613
+ // intersection may further constrain the constraints of the non-primitive types. For example, given a type
18614
+ // parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
18615
+ // appear to be comparable to '2'.
18616
+ if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
18617
+ const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
18618
+ if (constraints !== (source as IntersectionType).types) {
18619
+ source = getIntersectionType(constraints);
18620
+ if (!(source.flags & TypeFlags.Intersection)) {
18621
+ return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false);
18622
+ }
18623
+ }
18624
+ }
18625
+ // Check to see if any constituents of the intersection are immediately related to the target.
18626
+ //
18627
+ // Don't report errors though. Checking whether a constituent is related to the source is not actually
18628
+ // useful and leads to some confusing error messages. Instead it is better to let the below checks
18629
+ // take care of this, or to not elaborate at all. For instance,
18630
+ //
18631
+ // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
18632
+ //
18633
+ // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
18634
+ // than to report that 'D' is not assignable to 'A' or 'B'.
18635
+ //
18636
+ // - For a primitive type or type parameter (such as 'number = A & B') there is no point in
18637
+ // breaking the intersection apart.
18638
+ return someTypeRelatedToType(source as IntersectionType, target, /*reportErrors*/ false, IntersectionState.Source);
18639
+ }
18640
+
18609
18641
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
18610
18642
let result = Ternary.True;
18611
18643
const sourceTypes = source.types;
@@ -18903,49 +18935,23 @@ namespace ts {
18903
18935
if (intersectionState & IntersectionState.PropertyCheck) {
18904
18936
return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
18905
18937
}
18906
- if (intersectionState & IntersectionState.UnionIntersectionCheck) {
18907
- // Note that these checks are specifically ordered to produce correct results. In particular,
18908
- // we need to deconstruct unions before intersections (because unions are always at the top),
18909
- // and we need to handle "each" relations before "some" relations for the same kind of type.
18910
- if (source.flags & TypeFlags.Union) {
18911
- return relation === comparableRelation ?
18912
- someTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck) :
18913
- eachTypeRelatedToType(source as UnionType, target, reportErrors && !(source.flags & TypeFlags.Primitive), intersectionState & ~IntersectionState.UnionIntersectionCheck);
18914
- }
18915
- if (target.flags & TypeFlags.Union) {
18916
- return typeRelatedToSomeType(getRegularTypeOfObjectLiteral(source), target as UnionType, reportErrors && !(source.flags & TypeFlags.Primitive) && !(target.flags & TypeFlags.Primitive));
18917
- }
18918
- if (target.flags & TypeFlags.Intersection) {
18919
- return typeRelatedToEachType(getRegularTypeOfObjectLiteral(source), target as IntersectionType, reportErrors, IntersectionState.Target);
18920
- }
18921
- // Source is an intersection. For the comparable relation, if the target is a primitive type we hoist the
18922
- // constraints of all non-primitive types in the source into a new intersection. We do this because the
18923
- // intersection may further constrain the constraints of the non-primitive types. For example, given a type
18924
- // parameter 'T extends 1 | 2', the intersection 'T & 1' should be reduced to '1' such that it doesn't
18925
- // appear to be comparable to '2'.
18926
- if (relation === comparableRelation && target.flags & TypeFlags.Primitive) {
18927
- const constraints = sameMap((source as IntersectionType).types, getBaseConstraintOrType);
18928
- if (constraints !== (source as IntersectionType).types) {
18929
- source = getIntersectionType(constraints);
18930
- if (!(source.flags & TypeFlags.Intersection)) {
18931
- return isRelatedTo(source, target, RecursionFlags.Source, /*reportErrors*/ false);
18932
- }
18933
- }
18938
+ let result: Ternary;
18939
+ let originalErrorInfo: DiagnosticMessageChain | undefined;
18940
+ let varianceCheckFailed = false;
18941
+ const saveErrorInfo = captureErrorCalculationState();
18942
+ if (source.flags & TypeFlags.UnionOrIntersection || target.flags & TypeFlags.UnionOrIntersection) {
18943
+ result = unionOrIntersectionRelatedTo(source, target, reportErrors, intersectionState);
18944
+ // The ordered decomposition above doesn't handle all cases. Specifically, we also need to handle:
18945
+ // Source is instantiable (e.g. source has union or intersection constraint).
18946
+ // Source is an object, target is a union (e.g. { a, b: boolean } <=> { a, b: true } | { a, b: false }).
18947
+ // Source is an intersection, target is an object (e.g. { a } & { b } <=> { a, b }).
18948
+ // Source is an intersection, target is a union (e.g. { a } & { b: boolean } <=> { a, b: true } | { a, b: false }).
18949
+ // Source is an intersection, target instantiable (e.g. string & { tag } <=> T["a"] constrained to string & { tag }).
18950
+ if (result || !(source.flags & TypeFlags.Instantiable ||
18951
+ source.flags & TypeFlags.Object && target.flags & TypeFlags.Union ||
18952
+ source.flags & TypeFlags.Intersection && target.flags & (TypeFlags.Object | TypeFlags.Union | TypeFlags.Instantiable))) {
18953
+ return result;
18934
18954
}
18935
- // Check to see if any constituents of the intersection are immediately related to the target.
18936
- //
18937
- // Don't report errors though. Checking whether a constituent is related to the source is not actually
18938
- // useful and leads to some confusing error messages. Instead it is better to let the below checks
18939
- // take care of this, or to not elaborate at all. For instance,
18940
- //
18941
- // - For an object type (such as 'C = A & B'), users are usually more interested in structural errors.
18942
- //
18943
- // - For a union type (such as '(A | B) = (C & D)'), it's better to hold onto the whole intersection
18944
- // than to report that 'D' is not assignable to 'A' or 'B'.
18945
- //
18946
- // - For a primitive type or type parameter (such as 'number = A & B') there is no point in
18947
- // breaking the intersection apart.
18948
- return someTypeRelatedToType(source as IntersectionType, target, /*reportErrors*/ false, IntersectionState.Source);
18949
18955
}
18950
18956
const flags = source.flags & target.flags;
18951
18957
if (relation === identityRelation && !(flags & TypeFlags.Object)) {
@@ -18979,11 +18985,6 @@ namespace ts {
18979
18985
return Ternary.False;
18980
18986
}
18981
18987
18982
- let result: Ternary;
18983
- let originalErrorInfo: DiagnosticMessageChain | undefined;
18984
- let varianceCheckFailed = false;
18985
- const saveErrorInfo = captureErrorCalculationState();
18986
-
18987
18988
// We limit alias variance probing to only object and conditional types since their alias behavior
18988
18989
// is more predictable than other, interned types, which may or may not have an alias depending on
18989
18990
// the order in which things were checked.
@@ -23338,10 +23339,6 @@ namespace ts {
23338
23339
mapType(type, mapper);
23339
23340
}
23340
23341
23341
- function getConstituentCount(type: Type) {
23342
- return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1;
23343
- }
23344
-
23345
23342
function extractTypesOfKind(type: Type, kind: TypeFlags) {
23346
23343
return filterType(type, t => (t.flags & kind) !== 0);
23347
23344
}
0 commit comments