@@ -17258,14 +17258,29 @@ namespace ts {
17258
17258
return Ternary.False;
17259
17259
}
17260
17260
17261
+ function getUndefinedStrippedTargetIfNeeded(source: Type, target: Type) {
17262
+ // As a builtin type, `undefined` is a very low type ID - making it almsot always first, making this a very fast check to see
17263
+ // if we need to strip `undefined` from the target
17264
+ if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union &&
17265
+ !((source as UnionType).types[0].flags & TypeFlags.Undefined) && (target as UnionType).types[0].flags & TypeFlags.Undefined) {
17266
+ return extractTypesOfKind(target, ~TypeFlags.Undefined);
17267
+ }
17268
+ return target;
17269
+ }
17270
+
17261
17271
function eachTypeRelatedToType(source: UnionOrIntersectionType, target: Type, reportErrors: boolean, intersectionState: IntersectionState): Ternary {
17262
17272
let result = Ternary.True;
17263
17273
const sourceTypes = source.types;
17274
+ const undefinedStrippedTarget = getUndefinedStrippedTargetIfNeeded(source, target as UnionType);
17264
17275
for (let i = 0; i < sourceTypes.length; i++) {
17265
17276
const sourceType = sourceTypes[i];
17266
- if (target .flags & TypeFlags.Union && (target as UnionType).types.length === sourceTypes.length) {
17277
+ if (undefinedStrippedTarget .flags & TypeFlags.Union && sourceTypes.length >= (undefinedStrippedTarget as UnionType).types.length && sourceTypes.length % (undefinedStrippedTarget as UnionType).types.length === 0 ) {
17267
17278
// many unions are mappings of one another; in such cases, simply comparing members at the same index can shortcut the comparison
17268
- const related = isRelatedTo(sourceType, (target as UnionType).types[i], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
17279
+ // such unions will have identical lengths, and their corresponding elements will match up. Another common scenario is where a large
17280
+ // union has a union of objects intersected with it. In such cases, if the input was, eg `("a" | "b" | "c") & (string | boolean | {} | {whatever})`,
17281
+ // the result will have the structure `"a" | "b" | "c" | "a" & {} | "b" & {} | "c" & {} | "a" & {whatever} | "b" & {whatever} | "c" & {whatever}`
17282
+ // - the resulting union has a length which is a multiple of the original union, and the elements correspond modulo the length of the original union
17283
+ const related = isRelatedTo(sourceType, (undefinedStrippedTarget as UnionType).types[i % (undefinedStrippedTarget as UnionType).types.length], /*reportErrors*/ false, /*headMessage*/ undefined, intersectionState);
17269
17284
if (related) {
17270
17285
result &= related;
17271
17286
continue;
0 commit comments