@@ -247,6 +247,10 @@ public static function union(Type ...$types): Type
247
247
unset($ types [$ i ]);
248
248
}
249
249
250
+ foreach ($ scalarTypes as $ classType => $ scalarTypeItems ) {
251
+ $ scalarTypes [$ classType ] = array_values ($ scalarTypeItems );
252
+ }
253
+
250
254
/** @var ArrayType[] $arrayTypes */
251
255
$ arrayTypes = $ arrayTypes ;
252
256
@@ -280,105 +284,68 @@ public static function union(Type ...$types): Type
280
284
281
285
foreach ($ scalarTypes as $ classType => $ scalarTypeItems ) {
282
286
if (isset ($ hasGenericScalarTypes [$ classType ])) {
287
+ unset($ scalarTypes [$ classType ]);
283
288
continue ;
284
289
}
285
290
if ($ classType === ConstantBooleanType::class && count ($ scalarTypeItems ) === 2 ) {
286
291
$ types [] = new BooleanType ();
292
+ unset($ scalarTypes [$ classType ]);
287
293
continue ;
288
294
}
289
- foreach ($ scalarTypeItems as $ type ) {
290
- $ types [] = $ type ;
291
- }
292
- }
293
295
294
- // transform A | A to A
295
- // transform A | never to A
296
- for ($ i = 0 ; $ i < count ($ types ); $ i ++) {
297
- for ($ j = $ i + 1 ; $ j < count ($ types ); $ j ++) {
298
- if ($ types [$ i ] instanceof IntegerRangeType) {
299
- $ type = $ types [$ i ]->tryUnion ($ types [$ j ]);
300
- if ($ type !== null ) {
301
- $ types [$ i ] = $ type ;
302
- $ i --;
303
- array_splice ($ types , $ j , 1 );
304
- continue 2 ;
296
+ for ($ i = 0 ; $ i < count ($ scalarTypeItems ); $ i ++) {
297
+ for ($ j = 0 ; $ j < count ($ types ); $ j ++) {
298
+ $ compareResult = self ::compareTypesInUnion ($ scalarTypeItems [$ i ], $ types [$ j ]);
299
+ if ($ compareResult === null ) {
300
+ continue ;
305
301
}
306
- }
307
302
308
- if ($ types [$ i ] instanceof SubtractableType) {
309
- $ typeWithoutSubtractedTypeA = $ types [$ i ]->getTypeWithoutSubtractedType ();
310
- if ($ typeWithoutSubtractedTypeA instanceof MixedType && $ types [$ j ] instanceof MixedType) {
311
- $ isSuperType = $ typeWithoutSubtractedTypeA ->isSuperTypeOfMixed ($ types [$ j ]);
312
- } else {
313
- $ isSuperType = $ typeWithoutSubtractedTypeA ->isSuperTypeOf ($ types [$ j ]);
314
- }
315
- if ($ isSuperType ->yes ()) {
316
- $ subtractedType = null ;
317
- if ($ types [$ j ] instanceof SubtractableType) {
318
- $ subtractedType = $ types [$ j ]->getSubtractedType ();
319
- }
320
- $ types [$ i ] = self ::intersectWithSubtractedType ($ types [$ i ], $ subtractedType );
303
+ [$ a , $ b ] = $ compareResult ;
304
+ if ($ a !== null ) {
305
+ $ scalarTypeItems [$ i ] = $ a ;
321
306
array_splice ($ types , $ j --, 1 );
322
307
continue 1 ;
323
308
}
324
- }
325
-
326
- if ($ types [$ j ] instanceof SubtractableType) {
327
- $ typeWithoutSubtractedTypeB = $ types [$ j ]->getTypeWithoutSubtractedType ();
328
- if ($ typeWithoutSubtractedTypeB instanceof MixedType && $ types [$ i ] instanceof MixedType) {
329
- $ isSuperType = $ typeWithoutSubtractedTypeB ->isSuperTypeOfMixed ($ types [$ i ]);
330
- } else {
331
- $ isSuperType = $ typeWithoutSubtractedTypeB ->isSuperTypeOf ($ types [$ i ]);
332
- }
333
- if ($ isSuperType ->yes ()) {
334
- $ subtractedType = null ;
335
- if ($ types [$ i ] instanceof SubtractableType) {
336
- $ subtractedType = $ types [$ i ]->getSubtractedType ();
337
- }
338
- $ types [$ j ] = self ::intersectWithSubtractedType ($ types [$ j ], $ subtractedType );
339
- array_splice ($ types , $ i --, 1 );
309
+ if ($ b !== null ) {
310
+ $ types [$ j ] = $ b ;
311
+ array_splice ($ scalarTypeItems , $ i --, 1 );
340
312
continue 2 ;
341
313
}
342
314
}
315
+ }
343
316
344
- if (
345
- !$ types [$ j ] instanceof ConstantArrayType
346
- && $ types [$ j ]->isSuperTypeOf ($ types [$ i ])->yes ()
347
- ) {
348
- array_splice ($ types , $ i --, 1 );
349
- continue 2 ;
350
- }
317
+ $ scalarTypes [$ classType ] = $ scalarTypeItems ;
318
+ }
351
319
352
- if (
353
- !$ types [$ i ] instanceof ConstantArrayType
354
- && $ types [$ i ]->isSuperTypeOf ($ types [$ j ])->yes ()
355
- ) {
356
- array_splice ($ types , $ j --, 1 );
357
- continue 1 ;
320
+ // transform A | A to A
321
+ // transform A | never to A
322
+ for ($ i = 0 ; $ i < count ($ types ); $ i ++) {
323
+ for ($ j = $ i + 1 ; $ j < count ($ types ); $ j ++) {
324
+ $ compareResult = self ::compareTypesInUnion ($ types [$ i ], $ types [$ j ]);
325
+ if ($ compareResult === null ) {
326
+ continue ;
358
327
}
359
328
360
- if (
361
- $ types [$ i ] instanceof ConstantStringType
362
- && $ types [$ i ]->getValue () === ''
363
- && $ types [$ j ]->describe (VerbosityLevel::value ()) === 'non-empty-string '
364
- ) {
365
- $ types [$ i ] = new StringType ();
329
+ [$ a , $ b ] = $ compareResult ;
330
+ if ($ a !== null ) {
331
+ $ types [$ i ] = $ a ;
366
332
array_splice ($ types , $ j --, 1 );
367
333
continue 1 ;
368
334
}
369
-
370
- if (
371
- $ types [$ j ] instanceof ConstantStringType
372
- && $ types [$ j ]->getValue () === ''
373
- && $ types [$ i ]->describe (VerbosityLevel::value ()) === 'non-empty-string '
374
- ) {
375
- $ types [$ j ] = new StringType ();
335
+ if ($ b !== null ) {
336
+ $ types [$ j ] = $ b ;
376
337
array_splice ($ types , $ i --, 1 );
377
338
continue 2 ;
378
339
}
379
340
}
380
341
}
381
342
343
+ foreach ($ scalarTypes as $ scalarTypeItems ) {
344
+ foreach ($ scalarTypeItems as $ scalarType ) {
345
+ $ types [] = $ scalarType ;
346
+ }
347
+ }
348
+
382
349
if (count ($ types ) === 0 ) {
383
350
return new NeverType ();
384
351
@@ -408,6 +375,95 @@ public static function union(Type ...$types): Type
408
375
return new UnionType ($ types );
409
376
}
410
377
378
+ /**
379
+ * @param Type $a
380
+ * @param Type $b
381
+ * @return array{Type, null}|array{null, Type}|null
382
+ */
383
+ private static function compareTypesInUnion (Type $ a , Type $ b ): ?array
384
+ {
385
+ if ($ a instanceof IntegerRangeType) {
386
+ $ type = $ a ->tryUnion ($ b );
387
+ if ($ type !== null ) {
388
+ $ a = $ type ;
389
+ return [$ a , null ];
390
+ }
391
+ }
392
+ if ($ b instanceof IntegerRangeType) {
393
+ $ type = $ b ->tryUnion ($ a );
394
+ if ($ type !== null ) {
395
+ $ b = $ type ;
396
+ return [null , $ b ];
397
+ }
398
+ }
399
+
400
+ if ($ a instanceof SubtractableType) {
401
+ $ typeWithoutSubtractedTypeA = $ a ->getTypeWithoutSubtractedType ();
402
+ if ($ typeWithoutSubtractedTypeA instanceof MixedType && $ b instanceof MixedType) {
403
+ $ isSuperType = $ typeWithoutSubtractedTypeA ->isSuperTypeOfMixed ($ b );
404
+ } else {
405
+ $ isSuperType = $ typeWithoutSubtractedTypeA ->isSuperTypeOf ($ b );
406
+ }
407
+ if ($ isSuperType ->yes ()) {
408
+ $ subtractedType = null ;
409
+ if ($ b instanceof SubtractableType) {
410
+ $ subtractedType = $ b ->getSubtractedType ();
411
+ }
412
+ $ a = self ::intersectWithSubtractedType ($ a , $ subtractedType );
413
+ return [$ a , null ];
414
+ }
415
+ }
416
+
417
+ if ($ b instanceof SubtractableType) {
418
+ $ typeWithoutSubtractedTypeB = $ b ->getTypeWithoutSubtractedType ();
419
+ if ($ typeWithoutSubtractedTypeB instanceof MixedType && $ a instanceof MixedType) {
420
+ $ isSuperType = $ typeWithoutSubtractedTypeB ->isSuperTypeOfMixed ($ a );
421
+ } else {
422
+ $ isSuperType = $ typeWithoutSubtractedTypeB ->isSuperTypeOf ($ a );
423
+ }
424
+ if ($ isSuperType ->yes ()) {
425
+ $ subtractedType = null ;
426
+ if ($ a instanceof SubtractableType) {
427
+ $ subtractedType = $ a ->getSubtractedType ();
428
+ }
429
+ $ b = self ::intersectWithSubtractedType ($ b , $ subtractedType );
430
+ return [null , $ b ];
431
+ }
432
+ }
433
+
434
+ if (
435
+ !$ b instanceof ConstantArrayType
436
+ && $ b ->isSuperTypeOf ($ a )->yes ()
437
+ ) {
438
+ return [null , $ b ];
439
+ }
440
+
441
+ if (
442
+ !$ a instanceof ConstantArrayType
443
+ && $ a ->isSuperTypeOf ($ b )->yes ()
444
+ ) {
445
+ return [$ a , null ];
446
+ }
447
+
448
+ if (
449
+ $ a instanceof ConstantStringType
450
+ && $ a ->getValue () === ''
451
+ && $ b ->describe (VerbosityLevel::value ()) === 'non-empty-string '
452
+ ) {
453
+ return [null , new StringType ()];
454
+ }
455
+
456
+ if (
457
+ $ b instanceof ConstantStringType
458
+ && $ b ->getValue () === ''
459
+ && $ a ->describe (VerbosityLevel::value ()) === 'non-empty-string '
460
+ ) {
461
+ return [new StringType (), null ];
462
+ }
463
+
464
+ return null ;
465
+ }
466
+
411
467
private static function unionWithSubtractedType (
412
468
Type $ type ,
413
469
?Type $ subtractedType
0 commit comments