@@ -188,58 +188,12 @@ public static function union(Type ...$types): Type
188188 }
189189 }
190190
191- // Fast path for N>2: strip implicit NeverTypes and short-circuit on mixed
192- if ($ typesCount > 2 ) {
193- $ neverCount = 0 ;
194- $ hasUnionOrBenevolent = false ;
195- for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
196- $ t = $ types [$ i ];
197- if (
198- $ t instanceof MixedType
199- && !$ t ->isExplicitMixed ()
200- && !$ t instanceof TemplateMixedType
201- && $ t ->getSubtractedType () === null
202- ) {
203- return $ t ;
204- }
205- if ($ t instanceof NeverType && !$ t ->isExplicit ()) {
206- $ neverCount ++;
207- } elseif ($ t instanceof UnionType && !$ t instanceof TemplateType) {
208- $ hasUnionOrBenevolent = true ;
209- }
210- }
211-
212- if ($ neverCount > 0 && !$ hasUnionOrBenevolent ) {
213- if ($ neverCount === $ typesCount ) {
214- return new NeverType ();
215- }
216-
217- $ filtered = [];
218- for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
219- if ($ types [$ i ] instanceof NeverType && !$ types [$ i ]->isExplicit ()) {
220- continue ;
221- }
222-
223- $ filtered [] = $ types [$ i ];
224- }
225- $ filteredCount = count ($ filtered );
226-
227- if ($ filteredCount === 1 && !$ filtered [0 ]->isArray ()->yes ()) {
228- return $ filtered [0 ];
229- }
230- if ($ filteredCount === 2 ) {
231- return self ::union ($ filtered [0 ], $ filtered [1 ]);
232- }
233- $ types = $ filtered ;
234- $ typesCount = $ filteredCount ;
235- }
236- }
237-
238191 $ alreadyNormalized = [];
239192 $ alreadyNormalizedCounter = 0 ;
240193
241194 $ benevolentTypes = [];
242195 $ benevolentUnionObject = null ;
196+ $ neverCount = 0 ;
243197 // transform A | (B | C) to A | B | C
244198 for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
245199 if (
@@ -251,8 +205,7 @@ public static function union(Type ...$types): Type
251205 return $ types [$ i ];
252206 }
253207 if ($ types [$ i ] instanceof NeverType && !$ types [$ i ]->isExplicit ()) {
254- array_splice ($ types , $ i --, 1 );
255- $ typesCount --;
208+ $ neverCount ++;
256209 continue ;
257210 }
258211 if ($ types [$ i ] instanceof BenevolentUnionType) {
@@ -283,6 +236,33 @@ public static function union(Type ...$types): Type
283236 $ typesCount += count ($ typesInner ) - 1 ;
284237 }
285238
239+ // Bulk-remove implicit NeverTypes (skipped during the loop above)
240+ if ($ neverCount > 0 ) {
241+ if ($ neverCount === $ typesCount ) {
242+ return new NeverType ();
243+ }
244+
245+ $ filtered = [];
246+ for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
247+ if ($ types [$ i ] instanceof NeverType && !$ types [$ i ]->isExplicit ()) {
248+ continue ;
249+ }
250+ $ filtered [] = $ types [$ i ];
251+ }
252+ $ types = $ filtered ;
253+ $ typesCount = count ($ types );
254+
255+ if ($ typesCount === 0 ) {
256+ return new NeverType ();
257+ }
258+ if ($ typesCount === 1 && !$ types [0 ]->isArray ()->yes ()) {
259+ return $ types [0 ];
260+ }
261+ if ($ typesCount === 2 ) {
262+ return self ::union ($ types [0 ], $ types [1 ]);
263+ }
264+ }
265+
286266 if ($ typesCount === 0 ) {
287267 return new NeverType ();
288268 }
@@ -337,13 +317,17 @@ public static function union(Type ...$types): Type
337317 unset($ types [$ i ]);
338318 }
339319
340- $ enumCaseTypes = array_values ($ enumCaseTypes );
341- usort (
342- $ integerRangeTypes ,
343- static fn (IntegerRangeType $ a , IntegerRangeType $ b ): int => ($ a ->getMin () ?? PHP_INT_MIN ) <=> ($ b ->getMin () ?? PHP_INT_MIN )
344- ?: ($ a ->getMax () ?? PHP_INT_MAX ) <=> ($ b ->getMax () ?? PHP_INT_MAX ),
345- );
346- $ types = array_merge ($ types , $ integerRangeTypes );
320+ if ($ enumCaseTypes !== []) {
321+ $ enumCaseTypes = array_values ($ enumCaseTypes );
322+ }
323+ if ($ integerRangeTypes !== []) {
324+ usort (
325+ $ integerRangeTypes ,
326+ static fn (IntegerRangeType $ a , IntegerRangeType $ b ): int => ($ a ->getMin () ?? PHP_INT_MIN ) <=> ($ b ->getMin () ?? PHP_INT_MIN )
327+ ?: ($ a ->getMax () ?? PHP_INT_MAX ) <=> ($ b ->getMax () ?? PHP_INT_MAX ),
328+ );
329+ $ types = array_merge ($ types , $ integerRangeTypes );
330+ }
347331 $ types = array_values ($ types );
348332 $ typesCount = count ($ types );
349333
@@ -387,7 +371,7 @@ public static function union(Type ...$types): Type
387371 $ scalarTypes [$ classType ] = $ scalarTypeItems ;
388372 }
389373
390- if (count ($ types ) > 16 ) {
374+ if ($ typesCount > 16 && count ($ types ) > 16 ) {
391375 $ newTypes = [];
392376 foreach ($ types as $ type ) {
393377 $ newTypes [$ type ->describe (VerbosityLevel::cache ())] = $ type ;
@@ -403,28 +387,42 @@ public static function union(Type ...$types): Type
403387
404388 // transform A | A to A
405389 // transform A | never to A
406- for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
407- for ($ j = $ i + 1 ; $ j < $ typesCount ; $ j ++) {
408- if (self ::isAlreadyNormalized ($ alreadyNormalized , $ types [$ i ], $ types [$ j ])) {
409- continue ;
410- }
411- $ compareResult = self ::compareTypesInUnion ($ types [$ i ], $ types [$ j ]);
412- if ($ compareResult === null ) {
413- continue ;
414- }
415-
390+ if ($ typesCount === 2 && $ alreadyNormalized === []) {
391+ $ compareResult = self ::compareTypesInUnion ($ types [0 ], $ types [1 ]);
392+ if ($ compareResult !== null ) {
416393 [$ a , $ b ] = $ compareResult ;
417394 if ($ a !== null ) {
418- $ types [$ i ] = $ a ;
419- array_splice ($ types , $ j --, 1 );
420- $ typesCount --;
421- continue 1 ;
395+ $ types = [$ a ];
396+ $ typesCount = 1 ;
397+ } elseif ($ b !== null ) {
398+ $ types = [$ b ];
399+ $ typesCount = 1 ;
422400 }
423- if ($ b !== null ) {
424- $ types [$ j ] = $ b ;
425- array_splice ($ types , $ i --, 1 );
426- $ typesCount --;
427- continue 2 ;
401+ }
402+ } else {
403+ for ($ i = 0 ; $ i < $ typesCount ; $ i ++) {
404+ for ($ j = $ i + 1 ; $ j < $ typesCount ; $ j ++) {
405+ if (self ::isAlreadyNormalized ($ alreadyNormalized , $ types [$ i ], $ types [$ j ])) {
406+ continue ;
407+ }
408+ $ compareResult = self ::compareTypesInUnion ($ types [$ i ], $ types [$ j ]);
409+ if ($ compareResult === null ) {
410+ continue ;
411+ }
412+
413+ [$ a , $ b ] = $ compareResult ;
414+ if ($ a !== null ) {
415+ $ types [$ i ] = $ a ;
416+ array_splice ($ types , $ j --, 1 );
417+ $ typesCount --;
418+ continue 1 ;
419+ }
420+ if ($ b !== null ) {
421+ $ types [$ j ] = $ b ;
422+ array_splice ($ types , $ i --, 1 );
423+ $ typesCount --;
424+ continue 2 ;
425+ }
428426 }
429427 }
430428 }
0 commit comments