@@ -18346,30 +18346,17 @@ namespace ts {
1834618346 let result = Ternary.False;
1834718347 const saveErrorInfo = captureErrorCalculationState();
1834818348
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);
1836618354 }
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)) {
1837018356 resetErrorInfo(saveErrorInfo);
1837118357 }
1837218358 }
18359+
1837318360 if (!result && source.flags & (TypeFlags.Intersection | TypeFlags.TypeParameter)) {
1837418361 // The combined constraint of an intersection type is the intersection of the constraints of
1837518362 // the constituents. When an intersection type contains instantiable types with union type
@@ -18606,6 +18593,51 @@ namespace ts {
1860618593 return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent === container.valueDeclaration;
1860718594 }
1860818595
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+
1860918641 function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
1861018642 let result = Ternary.True;
1861118643 const sourceTypes = source.types;
@@ -18903,49 +18935,23 @@ namespace ts {
1890318935 if (intersectionState & IntersectionState.PropertyCheck) {
1890418936 return propertiesRelatedTo(source, target, reportErrors, /*excludedProperties*/ undefined, IntersectionState.None);
1890518937 }
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;
1893418954 }
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);
1894918955 }
1895018956 const flags = source.flags & target.flags;
1895118957 if (relation === identityRelation && !(flags & TypeFlags.Object)) {
@@ -18979,11 +18985,6 @@ namespace ts {
1897918985 return Ternary.False;
1898018986 }
1898118987
18982- let result: Ternary;
18983- let originalErrorInfo: DiagnosticMessageChain | undefined;
18984- let varianceCheckFailed = false;
18985- const saveErrorInfo = captureErrorCalculationState();
18986-
1898718988 // We limit alias variance probing to only object and conditional types since their alias behavior
1898818989 // is more predictable than other, interned types, which may or may not have an alias depending on
1898918990 // the order in which things were checked.
@@ -23338,10 +23339,6 @@ namespace ts {
2333823339 mapType(type, mapper);
2333923340 }
2334023341
23341- function getConstituentCount(type: Type) {
23342- return type.flags & TypeFlags.Union ? (type as UnionType).types.length : 1;
23343- }
23344-
2334523342 function extractTypesOfKind(type: Type, kind: TypeFlags) {
2334623343 return filterType(type, t => (t.flags & kind) !== 0);
2334723344 }
0 commit comments