From 60cde2d0412ab35cd3395b9765ed54cd371ec86b Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 25 Jul 2024 12:05:59 -0700 Subject: [PATCH 1/3] Expand divide and conquer intersection construction heuristic All the way down to 3 member intersections. This way you can only encounter a complexity error for a pick of two (or more) 1000 property types, and not three 50 property types. I played around with some other heuristics, but they ended up being overly costly for how narrow they were - pattern literals throw a wrench into our usual intersection fast path for literal types, and we *could* preserve it in cases like this one where there's a single pattern literal type common to all otherwise-primitive unions in the intersection (intersections with other literals either produce never or that literal), but that all goes away once there are two or more pattern literals, since then you need to worry about myrid ways the patterns can combine (which is to say: potentially as many ways as the power set indicates), so it didn't seem worth it for the complexity. Even `react` has *two* of these pattern index signatures (aria- and data-), not one, so such a heuristic won't help the common case anyway. I think there is still *some* splitting and pre-simplification we can do among the literal members, probably, even when there are non-primitive members, but I haven't been able to nail down a sufficiently general algorithm yet. So instead this one-liner will probably suffice for now. --- src/compiler/checker.ts | 2 +- .../reference/pickOfLargeObjectUnionWorks.js | 85 ++++++ .../pickOfLargeObjectUnionWorks.symbols | 226 ++++++++++++++ .../pickOfLargeObjectUnionWorks.types | 275 ++++++++++++++++++ .../compiler/pickOfLargeObjectUnionWorks.ts | 79 +++++ 5 files changed, 666 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/pickOfLargeObjectUnionWorks.js create mode 100644 tests/baselines/reference/pickOfLargeObjectUnionWorks.symbols create mode 100644 tests/baselines/reference/pickOfLargeObjectUnionWorks.types create mode 100644 tests/cases/compiler/pickOfLargeObjectUnionWorks.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7f05c9b40df23..8f96fbafc11c8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18117,7 +18117,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { removeFromEach(typeSet, TypeFlags.Null); result = getUnionType([getIntersectionType(typeSet, flags), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } - else if (typeSet.length >= 4) { + else if (typeSet.length >= 3) { // When we have four or more constituents, some of which are unions, we employ a "divide and conquer" strategy // where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can diff --git a/tests/baselines/reference/pickOfLargeObjectUnionWorks.js b/tests/baselines/reference/pickOfLargeObjectUnionWorks.js new file mode 100644 index 0000000000000..fbe9db615a0e7 --- /dev/null +++ b/tests/baselines/reference/pickOfLargeObjectUnionWorks.js @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/pickOfLargeObjectUnionWorks.ts] //// + +//// [pickOfLargeObjectUnionWorks.ts] +interface HTMLDataAttributes { + [data: `data-${string}`]: unknown +} + +interface Base { + id: string; + prop01: string; + prop02: string; + prop03: string; + prop04: string; + prop05: string; + prop06: string; + prop07: string; + prop08: string; + prop09: string; + prop10: string; + prop11: string; + prop12: string; + prop13: string; + prop14: string; + prop15: string; + prop16: string; + prop17: string; + prop18: string; + prop19: string; + prop20: string; + prop21: string; + prop22: string; + prop23: string; + prop24: string; + prop25: string; + prop26: string; + prop27: string; + prop28: string; + prop29: string; + prop30: string; + prop31: string; + prop32: string; + prop33: string; + prop34: string; + prop35: string; + prop36: string; + prop37: string; + } + + interface A extends Base, HTMLDataAttributes { + a1: string; + a2: string; + a3: string; + a4: string; + a5: string; + a6: string; + a7: string; + a8: string; + } + + interface B extends Base, HTMLDataAttributes { + b1: string; + b2: string; + b3: string; + b4: string; + b5: string; + b6: string; + b7: string; + b8: string; + } + + interface C extends Base, HTMLDataAttributes { + c1: string; + c2: string; + c3: string; + c4: string; + c5: string; + c6: string; + c7: string; + c8: string; + } + + const xyz: Pick = {id: 'id'} + +//// [pickOfLargeObjectUnionWorks.js] +var xyz = { id: 'id' }; diff --git a/tests/baselines/reference/pickOfLargeObjectUnionWorks.symbols b/tests/baselines/reference/pickOfLargeObjectUnionWorks.symbols new file mode 100644 index 0000000000000..dae25147b997b --- /dev/null +++ b/tests/baselines/reference/pickOfLargeObjectUnionWorks.symbols @@ -0,0 +1,226 @@ +//// [tests/cases/compiler/pickOfLargeObjectUnionWorks.ts] //// + +=== pickOfLargeObjectUnionWorks.ts === +interface HTMLDataAttributes { +>HTMLDataAttributes : Symbol(HTMLDataAttributes, Decl(pickOfLargeObjectUnionWorks.ts, 0, 0)) + + [data: `data-${string}`]: unknown +>data : Symbol(data, Decl(pickOfLargeObjectUnionWorks.ts, 1, 5)) +} + +interface Base { +>Base : Symbol(Base, Decl(pickOfLargeObjectUnionWorks.ts, 2, 1)) + + id: string; +>id : Symbol(Base.id, Decl(pickOfLargeObjectUnionWorks.ts, 4, 16)) + + prop01: string; +>prop01 : Symbol(Base.prop01, Decl(pickOfLargeObjectUnionWorks.ts, 5, 15)) + + prop02: string; +>prop02 : Symbol(Base.prop02, Decl(pickOfLargeObjectUnionWorks.ts, 6, 19)) + + prop03: string; +>prop03 : Symbol(Base.prop03, Decl(pickOfLargeObjectUnionWorks.ts, 7, 19)) + + prop04: string; +>prop04 : Symbol(Base.prop04, Decl(pickOfLargeObjectUnionWorks.ts, 8, 19)) + + prop05: string; +>prop05 : Symbol(Base.prop05, Decl(pickOfLargeObjectUnionWorks.ts, 9, 19)) + + prop06: string; +>prop06 : Symbol(Base.prop06, Decl(pickOfLargeObjectUnionWorks.ts, 10, 19)) + + prop07: string; +>prop07 : Symbol(Base.prop07, Decl(pickOfLargeObjectUnionWorks.ts, 11, 19)) + + prop08: string; +>prop08 : Symbol(Base.prop08, Decl(pickOfLargeObjectUnionWorks.ts, 12, 19)) + + prop09: string; +>prop09 : Symbol(Base.prop09, Decl(pickOfLargeObjectUnionWorks.ts, 13, 19)) + + prop10: string; +>prop10 : Symbol(Base.prop10, Decl(pickOfLargeObjectUnionWorks.ts, 14, 19)) + + prop11: string; +>prop11 : Symbol(Base.prop11, Decl(pickOfLargeObjectUnionWorks.ts, 15, 19)) + + prop12: string; +>prop12 : Symbol(Base.prop12, Decl(pickOfLargeObjectUnionWorks.ts, 16, 19)) + + prop13: string; +>prop13 : Symbol(Base.prop13, Decl(pickOfLargeObjectUnionWorks.ts, 17, 19)) + + prop14: string; +>prop14 : Symbol(Base.prop14, Decl(pickOfLargeObjectUnionWorks.ts, 18, 19)) + + prop15: string; +>prop15 : Symbol(Base.prop15, Decl(pickOfLargeObjectUnionWorks.ts, 19, 19)) + + prop16: string; +>prop16 : Symbol(Base.prop16, Decl(pickOfLargeObjectUnionWorks.ts, 20, 19)) + + prop17: string; +>prop17 : Symbol(Base.prop17, Decl(pickOfLargeObjectUnionWorks.ts, 21, 19)) + + prop18: string; +>prop18 : Symbol(Base.prop18, Decl(pickOfLargeObjectUnionWorks.ts, 22, 19)) + + prop19: string; +>prop19 : Symbol(Base.prop19, Decl(pickOfLargeObjectUnionWorks.ts, 23, 19)) + + prop20: string; +>prop20 : Symbol(Base.prop20, Decl(pickOfLargeObjectUnionWorks.ts, 24, 19)) + + prop21: string; +>prop21 : Symbol(Base.prop21, Decl(pickOfLargeObjectUnionWorks.ts, 25, 19)) + + prop22: string; +>prop22 : Symbol(Base.prop22, Decl(pickOfLargeObjectUnionWorks.ts, 26, 19)) + + prop23: string; +>prop23 : Symbol(Base.prop23, Decl(pickOfLargeObjectUnionWorks.ts, 27, 19)) + + prop24: string; +>prop24 : Symbol(Base.prop24, Decl(pickOfLargeObjectUnionWorks.ts, 28, 19)) + + prop25: string; +>prop25 : Symbol(Base.prop25, Decl(pickOfLargeObjectUnionWorks.ts, 29, 19)) + + prop26: string; +>prop26 : Symbol(Base.prop26, Decl(pickOfLargeObjectUnionWorks.ts, 30, 19)) + + prop27: string; +>prop27 : Symbol(Base.prop27, Decl(pickOfLargeObjectUnionWorks.ts, 31, 19)) + + prop28: string; +>prop28 : Symbol(Base.prop28, Decl(pickOfLargeObjectUnionWorks.ts, 32, 19)) + + prop29: string; +>prop29 : Symbol(Base.prop29, Decl(pickOfLargeObjectUnionWorks.ts, 33, 19)) + + prop30: string; +>prop30 : Symbol(Base.prop30, Decl(pickOfLargeObjectUnionWorks.ts, 34, 19)) + + prop31: string; +>prop31 : Symbol(Base.prop31, Decl(pickOfLargeObjectUnionWorks.ts, 35, 19)) + + prop32: string; +>prop32 : Symbol(Base.prop32, Decl(pickOfLargeObjectUnionWorks.ts, 36, 19)) + + prop33: string; +>prop33 : Symbol(Base.prop33, Decl(pickOfLargeObjectUnionWorks.ts, 37, 19)) + + prop34: string; +>prop34 : Symbol(Base.prop34, Decl(pickOfLargeObjectUnionWorks.ts, 38, 19)) + + prop35: string; +>prop35 : Symbol(Base.prop35, Decl(pickOfLargeObjectUnionWorks.ts, 39, 19)) + + prop36: string; +>prop36 : Symbol(Base.prop36, Decl(pickOfLargeObjectUnionWorks.ts, 40, 19)) + + prop37: string; +>prop37 : Symbol(Base.prop37, Decl(pickOfLargeObjectUnionWorks.ts, 41, 19)) + } + + interface A extends Base, HTMLDataAttributes { +>A : Symbol(A, Decl(pickOfLargeObjectUnionWorks.ts, 43, 3)) +>Base : Symbol(Base, Decl(pickOfLargeObjectUnionWorks.ts, 2, 1)) +>HTMLDataAttributes : Symbol(HTMLDataAttributes, Decl(pickOfLargeObjectUnionWorks.ts, 0, 0)) + + a1: string; +>a1 : Symbol(A.a1, Decl(pickOfLargeObjectUnionWorks.ts, 45, 48)) + + a2: string; +>a2 : Symbol(A.a2, Decl(pickOfLargeObjectUnionWorks.ts, 46, 15)) + + a3: string; +>a3 : Symbol(A.a3, Decl(pickOfLargeObjectUnionWorks.ts, 47, 15)) + + a4: string; +>a4 : Symbol(A.a4, Decl(pickOfLargeObjectUnionWorks.ts, 48, 15)) + + a5: string; +>a5 : Symbol(A.a5, Decl(pickOfLargeObjectUnionWorks.ts, 49, 15)) + + a6: string; +>a6 : Symbol(A.a6, Decl(pickOfLargeObjectUnionWorks.ts, 50, 15)) + + a7: string; +>a7 : Symbol(A.a7, Decl(pickOfLargeObjectUnionWorks.ts, 51, 15)) + + a8: string; +>a8 : Symbol(A.a8, Decl(pickOfLargeObjectUnionWorks.ts, 52, 15)) + } + + interface B extends Base, HTMLDataAttributes { +>B : Symbol(B, Decl(pickOfLargeObjectUnionWorks.ts, 54, 3)) +>Base : Symbol(Base, Decl(pickOfLargeObjectUnionWorks.ts, 2, 1)) +>HTMLDataAttributes : Symbol(HTMLDataAttributes, Decl(pickOfLargeObjectUnionWorks.ts, 0, 0)) + + b1: string; +>b1 : Symbol(B.b1, Decl(pickOfLargeObjectUnionWorks.ts, 56, 48)) + + b2: string; +>b2 : Symbol(B.b2, Decl(pickOfLargeObjectUnionWorks.ts, 57, 15)) + + b3: string; +>b3 : Symbol(B.b3, Decl(pickOfLargeObjectUnionWorks.ts, 58, 15)) + + b4: string; +>b4 : Symbol(B.b4, Decl(pickOfLargeObjectUnionWorks.ts, 59, 15)) + + b5: string; +>b5 : Symbol(B.b5, Decl(pickOfLargeObjectUnionWorks.ts, 60, 15)) + + b6: string; +>b6 : Symbol(B.b6, Decl(pickOfLargeObjectUnionWorks.ts, 61, 15)) + + b7: string; +>b7 : Symbol(B.b7, Decl(pickOfLargeObjectUnionWorks.ts, 62, 15)) + + b8: string; +>b8 : Symbol(B.b8, Decl(pickOfLargeObjectUnionWorks.ts, 63, 15)) + } + + interface C extends Base, HTMLDataAttributes { +>C : Symbol(C, Decl(pickOfLargeObjectUnionWorks.ts, 65, 3)) +>Base : Symbol(Base, Decl(pickOfLargeObjectUnionWorks.ts, 2, 1)) +>HTMLDataAttributes : Symbol(HTMLDataAttributes, Decl(pickOfLargeObjectUnionWorks.ts, 0, 0)) + + c1: string; +>c1 : Symbol(C.c1, Decl(pickOfLargeObjectUnionWorks.ts, 67, 48)) + + c2: string; +>c2 : Symbol(C.c2, Decl(pickOfLargeObjectUnionWorks.ts, 68, 15)) + + c3: string; +>c3 : Symbol(C.c3, Decl(pickOfLargeObjectUnionWorks.ts, 69, 15)) + + c4: string; +>c4 : Symbol(C.c4, Decl(pickOfLargeObjectUnionWorks.ts, 70, 15)) + + c5: string; +>c5 : Symbol(C.c5, Decl(pickOfLargeObjectUnionWorks.ts, 71, 15)) + + c6: string; +>c6 : Symbol(C.c6, Decl(pickOfLargeObjectUnionWorks.ts, 72, 15)) + + c7: string; +>c7 : Symbol(C.c7, Decl(pickOfLargeObjectUnionWorks.ts, 73, 15)) + + c8: string; +>c8 : Symbol(C.c8, Decl(pickOfLargeObjectUnionWorks.ts, 74, 15)) + } + + const xyz: Pick = {id: 'id'} +>xyz : Symbol(xyz, Decl(pickOfLargeObjectUnionWorks.ts, 78, 7)) +>Pick : Symbol(Pick, Decl(lib.es5.d.ts, --, --)) +>A : Symbol(A, Decl(pickOfLargeObjectUnionWorks.ts, 43, 3)) +>B : Symbol(B, Decl(pickOfLargeObjectUnionWorks.ts, 54, 3)) +>C : Symbol(C, Decl(pickOfLargeObjectUnionWorks.ts, 65, 3)) +>id : Symbol(id, Decl(pickOfLargeObjectUnionWorks.ts, 78, 38)) + diff --git a/tests/baselines/reference/pickOfLargeObjectUnionWorks.types b/tests/baselines/reference/pickOfLargeObjectUnionWorks.types new file mode 100644 index 0000000000000..3a637bd997b8f --- /dev/null +++ b/tests/baselines/reference/pickOfLargeObjectUnionWorks.types @@ -0,0 +1,275 @@ +//// [tests/cases/compiler/pickOfLargeObjectUnionWorks.ts] //// + +=== pickOfLargeObjectUnionWorks.ts === +interface HTMLDataAttributes { + [data: `data-${string}`]: unknown +>data : `data-${string}` +> : ^^^^^^^^^^^^^^^^ +} + +interface Base { + id: string; +>id : string +> : ^^^^^^ + + prop01: string; +>prop01 : string +> : ^^^^^^ + + prop02: string; +>prop02 : string +> : ^^^^^^ + + prop03: string; +>prop03 : string +> : ^^^^^^ + + prop04: string; +>prop04 : string +> : ^^^^^^ + + prop05: string; +>prop05 : string +> : ^^^^^^ + + prop06: string; +>prop06 : string +> : ^^^^^^ + + prop07: string; +>prop07 : string +> : ^^^^^^ + + prop08: string; +>prop08 : string +> : ^^^^^^ + + prop09: string; +>prop09 : string +> : ^^^^^^ + + prop10: string; +>prop10 : string +> : ^^^^^^ + + prop11: string; +>prop11 : string +> : ^^^^^^ + + prop12: string; +>prop12 : string +> : ^^^^^^ + + prop13: string; +>prop13 : string +> : ^^^^^^ + + prop14: string; +>prop14 : string +> : ^^^^^^ + + prop15: string; +>prop15 : string +> : ^^^^^^ + + prop16: string; +>prop16 : string +> : ^^^^^^ + + prop17: string; +>prop17 : string +> : ^^^^^^ + + prop18: string; +>prop18 : string +> : ^^^^^^ + + prop19: string; +>prop19 : string +> : ^^^^^^ + + prop20: string; +>prop20 : string +> : ^^^^^^ + + prop21: string; +>prop21 : string +> : ^^^^^^ + + prop22: string; +>prop22 : string +> : ^^^^^^ + + prop23: string; +>prop23 : string +> : ^^^^^^ + + prop24: string; +>prop24 : string +> : ^^^^^^ + + prop25: string; +>prop25 : string +> : ^^^^^^ + + prop26: string; +>prop26 : string +> : ^^^^^^ + + prop27: string; +>prop27 : string +> : ^^^^^^ + + prop28: string; +>prop28 : string +> : ^^^^^^ + + prop29: string; +>prop29 : string +> : ^^^^^^ + + prop30: string; +>prop30 : string +> : ^^^^^^ + + prop31: string; +>prop31 : string +> : ^^^^^^ + + prop32: string; +>prop32 : string +> : ^^^^^^ + + prop33: string; +>prop33 : string +> : ^^^^^^ + + prop34: string; +>prop34 : string +> : ^^^^^^ + + prop35: string; +>prop35 : string +> : ^^^^^^ + + prop36: string; +>prop36 : string +> : ^^^^^^ + + prop37: string; +>prop37 : string +> : ^^^^^^ + } + + interface A extends Base, HTMLDataAttributes { + a1: string; +>a1 : string +> : ^^^^^^ + + a2: string; +>a2 : string +> : ^^^^^^ + + a3: string; +>a3 : string +> : ^^^^^^ + + a4: string; +>a4 : string +> : ^^^^^^ + + a5: string; +>a5 : string +> : ^^^^^^ + + a6: string; +>a6 : string +> : ^^^^^^ + + a7: string; +>a7 : string +> : ^^^^^^ + + a8: string; +>a8 : string +> : ^^^^^^ + } + + interface B extends Base, HTMLDataAttributes { + b1: string; +>b1 : string +> : ^^^^^^ + + b2: string; +>b2 : string +> : ^^^^^^ + + b3: string; +>b3 : string +> : ^^^^^^ + + b4: string; +>b4 : string +> : ^^^^^^ + + b5: string; +>b5 : string +> : ^^^^^^ + + b6: string; +>b6 : string +> : ^^^^^^ + + b7: string; +>b7 : string +> : ^^^^^^ + + b8: string; +>b8 : string +> : ^^^^^^ + } + + interface C extends Base, HTMLDataAttributes { + c1: string; +>c1 : string +> : ^^^^^^ + + c2: string; +>c2 : string +> : ^^^^^^ + + c3: string; +>c3 : string +> : ^^^^^^ + + c4: string; +>c4 : string +> : ^^^^^^ + + c5: string; +>c5 : string +> : ^^^^^^ + + c6: string; +>c6 : string +> : ^^^^^^ + + c7: string; +>c7 : string +> : ^^^^^^ + + c8: string; +>c8 : string +> : ^^^^^^ + } + + const xyz: Pick = {id: 'id'} +>xyz : Pick +> : ^^^^^^^^^^^^^^^^^^^^^ +>{id: 'id'} : { id: string; } +> : ^^^^^^^^^^^^^^^ +>id : string +> : ^^^^^^ +>'id' : "id" +> : ^^^^ + diff --git a/tests/cases/compiler/pickOfLargeObjectUnionWorks.ts b/tests/cases/compiler/pickOfLargeObjectUnionWorks.ts new file mode 100644 index 0000000000000..b416fac1ed680 --- /dev/null +++ b/tests/cases/compiler/pickOfLargeObjectUnionWorks.ts @@ -0,0 +1,79 @@ +interface HTMLDataAttributes { + [data: `data-${string}`]: unknown +} + +interface Base { + id: string; + prop01: string; + prop02: string; + prop03: string; + prop04: string; + prop05: string; + prop06: string; + prop07: string; + prop08: string; + prop09: string; + prop10: string; + prop11: string; + prop12: string; + prop13: string; + prop14: string; + prop15: string; + prop16: string; + prop17: string; + prop18: string; + prop19: string; + prop20: string; + prop21: string; + prop22: string; + prop23: string; + prop24: string; + prop25: string; + prop26: string; + prop27: string; + prop28: string; + prop29: string; + prop30: string; + prop31: string; + prop32: string; + prop33: string; + prop34: string; + prop35: string; + prop36: string; + prop37: string; + } + + interface A extends Base, HTMLDataAttributes { + a1: string; + a2: string; + a3: string; + a4: string; + a5: string; + a6: string; + a7: string; + a8: string; + } + + interface B extends Base, HTMLDataAttributes { + b1: string; + b2: string; + b3: string; + b4: string; + b5: string; + b6: string; + b7: string; + b8: string; + } + + interface C extends Base, HTMLDataAttributes { + c1: string; + c2: string; + c3: string; + c4: string; + c5: string; + c6: string; + c7: string; + c8: string; + } + + const xyz: Pick = {id: 'id'} \ No newline at end of file From d9d8c7743dcaba305ca04022cc7569789c7b6944 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 25 Jul 2024 13:07:07 -0700 Subject: [PATCH 2/3] How in the world was this avoiding reentrancy before? Chance? --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8f96fbafc11c8..7471762e2dcfe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18117,7 +18117,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { removeFromEach(typeSet, TypeFlags.Null); result = getUnionType([getIntersectionType(typeSet, flags), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } - else if (typeSet.length >= 3) { + else if (typeSet.length >= 3 && types.length > 2) { // When we have four or more constituents, some of which are unions, we employ a "divide and conquer" strategy // where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can From 9fdf7d5e03e44cac9b1b4f3dad1d4436517abd96 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 1 Aug 2024 14:17:55 -0700 Subject: [PATCH 3/3] Comment update --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 7471762e2dcfe..181e5a6665d47 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18118,7 +18118,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = getUnionType([getIntersectionType(typeSet, flags), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments); } else if (typeSet.length >= 3 && types.length > 2) { - // When we have four or more constituents, some of which are unions, we employ a "divide and conquer" strategy + // When we have three or more constituents, more than two inputs (to head off infinite reexpansion), some of which are unions, we employ a "divide and conquer" strategy // where A & B & C & D is processed as (A & B) & (C & D). Since intersections of unions often produce far smaller // unions of intersections than the full cartesian product (due to some intersections becoming `never`), this can // dramatically reduce the overall work.