@@ -16827,36 +16827,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
16827
16827
result = getUnionType([getIntersectionType(typeSet), nullType], UnionReduction.Literal, aliasSymbol, aliasTypeArguments);
16828
16828
}
16829
16829
else {
16830
- if (typeSet.length > 2 && getCrossProductUnionSize(typeSet) >= UNION_CROSS_PRODUCT_SIZE_LIMIT && every(typeSet, t => !!(t.flags & TypeFlags.Union) || !!(t.flags & TypeFlags.Primitive))) {
16831
- // This type set is going to trigger an "expression too complex" error below. Rather than resort to that, as a last, best effort, simplify the type.
16832
- // When the intersection looks like (A | B | C) & (D | E | F) & (G | H | I) - in the general case, this can result in a massive resulting
16833
- // union, hence the check on the cross product size below, _however_ in some cases we can also _simplify_ the resulting type massively.
16834
- // If we can recognize that upfront, we can still allow the type to form without creating innumerable intermediate types.
16835
- // Specifically, in cases where almost all combinations are known to reduce to `never` (so the result is essentially sparse)
16836
- // and we can recognize that quickly, we can use a simplified result without checking the worst-case size.
16837
- // So we start with the assumption that the result _is_ sparse when the input looks like the above, and we assume the result
16838
- // will take the form (A & D & G) | (B & E & H) | (C & F & I). To validate this, we reduce left, first combining
16839
- // (A | B | C) & (D | E | F); if that combines into `(A & D) | (B & E) | (C & F)` like we want, which we make 9 intermediate
16840
- // types to check, we can then combine the reduced `(A & D) | (B & E) | (C & F)` with (G | H | I), which again takes 9 intermediate types
16841
- // to check, finally producing `(A & D & G) | (B & E & H) | (C & F & I)`. This required 18 intermediate types, while the standard method
16842
- // of expanding (A | B | C) & (D | E | F) & (G | H | I) would produce 27 types and then perform reduction on the result.
16843
- // By going elemnt-wise, and bailing if the result fails to reduce, we can allow these sparse expansions without doing undue work.
16844
- runningResult = typeSet[0];
16845
- for (let i = 1; i < typeSet.length; i++) {
16846
- // For intersection reduction, here we're considering `undefined & (A | B)` as `never`. (ie, we're disallowing branded primitives)
16847
- // This is relevant for, eg, when looking at `(HTMLElement | null) & (SVGElement | null) & ... & undefined` where _usually_
16848
- // we'd allow for tons of garbage intermediate types like `null & SVGElement` to exist; but nobody ever really actually _wants_
16849
- // that, IMO. Those types can still exist in the type system; just... not when working with unions and intersections with massive
16850
- // cross-product growth potential.
16851
- runningResult = typeSet[i].flags & TypeFlags.Primitive && everyType(runningResult, t => !!(t.flags & TypeFlags.Object)) ? neverType : getReducedType(intersectTypes(runningResult, typeSet[i]));
16852
- if (i === typeSet.length - 1 || isTypeAny(runningResult) || runningResult.flags & TypeFlags.Never) {
16853
- return runningResult;
16854
- }
16855
- if (!(runningResult.flags & TypeFlags.Union) || (runningResult as UnionType).types.length > typeSet.length) {
16856
- // Save work done by the accumulated result thus far, even if we're bailing on the heuristic.
16857
- // It may have saved us enough work already that we're willing to work with the type now.
16858
- typeSet = typeSet.slice(i + 1);
16859
- break;
16830
+ if (getCrossProductUnionSize(typeSet) >= UNION_CROSS_PRODUCT_SIZE_LIMIT && every(typeSet, t => !!(t.flags & TypeFlags.Union) || !!(t.flags & TypeFlags.Primitive))) {
16831
+ if (typeSet.length > 2 || some(typeSet, t => getReducedType(t) !== t)) {
16832
+ // This type set is going to trigger an "expression too complex" error below. Rather than resort to that, as a last, best effort, simplify the type.
16833
+ // When the intersection looks like (A | B | C) & (D | E | F) & (G | H | I) - in the general case, this can result in a massive resulting
16834
+ // union, hence the check on the cross product size below, _however_ in some cases we can also _simplify_ the resulting type massively.
16835
+ // If we can recognize that upfront, we can still allow the type to form without creating innumerable intermediate types.
16836
+ // Specifically, in cases where almost all combinations are known to reduce to `never` (so the result is essentially sparse)
16837
+ // and we can recognize that quickly, we can use a simplified result without checking the worst-case size.
16838
+ // So we start with the assumption that the result _is_ sparse when the input looks like the above, and we assume the result
16839
+ // will take the form (A & D & G) | (B & E & H) | (C & F & I). To validate this, we reduce left, first combining
16840
+ // (A | B | C) & (D | E | F); if that combines into `(A & D) | (B & E) | (C & F)` like we want, which we make 9 intermediate
16841
+ // types to check, we can then combine the reduced `(A & D) | (B & E) | (C & F)` with (G | H | I), which again takes 9 intermediate types
16842
+ // to check, finally producing `(A & D & G) | (B & E & H) | (C & F & I)`. This required 18 intermediate types, while the standard method
16843
+ // of expanding (A | B | C) & (D | E | F) & (G | H | I) would produce 27 types and then perform reduction on the result.
16844
+ // By going elemnt-wise, and bailing if the result fails to reduce, we can allow these sparse expansions without doing undue work.
16845
+ runningResult = getReducedType(typeSet[0]);
16846
+ for (let i = 1; i < typeSet.length; i++) {
16847
+ // For intersection reduction, here we're considering `undefined & (A | B)` as `never`. (ie, we're disallowing branded primitives)
16848
+ // This is relevant for, eg, when looking at `(HTMLElement | null) & (SVGElement | null) & ... & undefined` where _usually_
16849
+ // we'd allow for tons of garbage intermediate types like `null & SVGElement` to exist; but nobody ever really actually _wants_
16850
+ // that, IMO. Those types can still exist in the type system; just... not when working with unions and intersections with massive
16851
+ // cross-product growth potential.
16852
+ const reducedElem = getReducedType(typeSet[i]);
16853
+ runningResult = reducedElem.flags & TypeFlags.Primitive && everyType(runningResult, t => !!(t.flags & TypeFlags.Object)) ? neverType : getReducedType(intersectTypes(runningResult, reducedElem));
16854
+ if (i === typeSet.length - 1 || isTypeAny(runningResult) || runningResult.flags & TypeFlags.Never) {
16855
+ return runningResult;
16856
+ }
16857
+ if (!(runningResult.flags & TypeFlags.Union) || (runningResult as UnionType).types.length > typeSet.length) {
16858
+ // Save work done by the accumulated result thus far, even if we're bailing on the heuristic.
16859
+ // It may have saved us enough work already that we're willing to work with the type now.
16860
+ typeSet = typeSet.slice(i + 1);
16861
+ break;
16862
+ }
16860
16863
}
16861
16864
}
16862
16865
}
0 commit comments