@@ -1549,25 +1549,38 @@ public static function intersect(Type ...$types): Type
15491549 $ constArray = $ constArrayIsI ? $ types [$ i ] : $ types [$ j ];
15501550 $ otherArray = $ constArrayIsI ? $ types [$ j ] : $ types [$ i ];
15511551
1552- $ newArray = ConstantArrayTypeBuilder::createEmpty ();
1553- $ valueTypes = $ constArray ->getValueTypes ();
1554- foreach ($ constArray ->getKeyTypes () as $ k => $ keyType ) {
1555- $ hasOffset = $ otherArray ->hasOffsetValueType ($ keyType );
1556- if ($ hasOffset ->no ()) {
1557- continue ;
1552+ if (
1553+ $ otherArray instanceof ConstantArrayType
1554+ && !$ constArray ->isUnsealed ()->maybe ()
1555+ && !$ otherArray ->isUnsealed ()->maybe ()
1556+ ) {
1557+ $ merged = self ::intersectDefiniteConstantArrays ($ constArray , $ otherArray );
1558+ if ($ merged instanceof NeverType) {
1559+ return $ merged ;
15581560 }
1559- $ newArray ->setOffsetValueType (
1560- self ::intersect ($ keyType , $ otherArray ->getIterableKeyType ()),
1561- self ::intersect ($ valueTypes [$ k ], $ otherArray ->getOffsetValueType ($ keyType )),
1562- $ constArray ->isOptionalKey ($ k ) && !$ hasOffset ->yes (),
1563- );
1561+ $ newArrayType = $ merged ;
1562+ } else {
1563+ $ newArray = ConstantArrayTypeBuilder::createEmpty ();
1564+ $ valueTypes = $ constArray ->getValueTypes ();
1565+ foreach ($ constArray ->getKeyTypes () as $ k => $ keyType ) {
1566+ $ hasOffset = $ otherArray ->hasOffsetValueType ($ keyType );
1567+ if ($ hasOffset ->no ()) {
1568+ continue ;
1569+ }
1570+ $ newArray ->setOffsetValueType (
1571+ self ::intersect ($ keyType , $ otherArray ->getIterableKeyType ()),
1572+ self ::intersect ($ valueTypes [$ k ], $ otherArray ->getOffsetValueType ($ keyType )),
1573+ $ constArray ->isOptionalKey ($ k ) && !$ hasOffset ->yes (),
1574+ );
1575+ }
1576+ $ newArrayType = $ newArray ->getArray ();
15641577 }
15651578
15661579 if ($ constArrayIsI ) {
1567- $ types [$ i ] = $ newArray -> getArray () ;
1580+ $ types [$ i ] = $ newArrayType ;
15681581 array_splice ($ types , $ j --, 1 );
15691582 } else {
1570- $ types [$ j ] = $ newArray -> getArray () ;
1583+ $ types [$ j ] = $ newArrayType ;
15711584 array_splice ($ types , $ i --, 1 );
15721585 }
15731586 $ typesCount --;
@@ -1634,6 +1647,121 @@ public static function intersect(Type ...$types): Type
16341647 return new IntersectionType ($ types );
16351648 }
16361649
1650+ private static function intersectDefiniteConstantArrays (ConstantArrayType $ a , ConstantArrayType $ b ): Type
1651+ {
1652+ $ aSealed = $ a ->isUnsealed ()->no ();
1653+ $ bSealed = $ b ->isUnsealed ()->no ();
1654+ $ bothUnsealed = !$ aSealed && !$ bSealed ;
1655+
1656+ $ aKeyByValue = [];
1657+ foreach ($ a ->getKeyTypes () as $ k => $ keyType ) {
1658+ $ aKeyByValue [$ keyType ->getValue ()] = $ k ;
1659+ }
1660+ $ bKeyByValue = [];
1661+ foreach ($ b ->getKeyTypes () as $ k => $ keyType ) {
1662+ $ bKeyByValue [$ keyType ->getValue ()] = $ k ;
1663+ }
1664+
1665+ if ($ aSealed && $ bSealed ) {
1666+ foreach ($ aKeyByValue as $ keyValue => $ k ) {
1667+ if (!$ a ->isOptionalKey ($ k ) && !array_key_exists ($ keyValue , $ bKeyByValue )) {
1668+ return new NeverType ();
1669+ }
1670+ }
1671+ foreach ($ bKeyByValue as $ keyValue => $ k ) {
1672+ if (!$ b ->isOptionalKey ($ k ) && !array_key_exists ($ keyValue , $ aKeyByValue )) {
1673+ return new NeverType ();
1674+ }
1675+ }
1676+ }
1677+
1678+ $ newArray = ConstantArrayTypeBuilder::createEmpty ();
1679+
1680+ if ($ bothUnsealed ) {
1681+ $ aUnsealed = $ a ->getUnsealedTypes ();
1682+ $ bUnsealed = $ b ->getUnsealedTypes ();
1683+ $ unsealedKey = self ::intersect ($ aUnsealed [0 ], $ bUnsealed [0 ]);
1684+ $ unsealedValue = self ::intersect ($ aUnsealed [1 ], $ bUnsealed [1 ]);
1685+ if ($ unsealedKey instanceof NeverType || $ unsealedValue instanceof NeverType) {
1686+ return new NeverType ();
1687+ }
1688+ $ newArray ->makeUnsealed ($ unsealedKey , $ unsealedValue );
1689+ } else {
1690+ $ never = new NeverType (true );
1691+ $ newArray ->makeUnsealed ($ never , $ never );
1692+ }
1693+
1694+ $ resolveOtherValue = static function (ConstantArrayType $ other , Type $ keyType ): ?Type {
1695+ if ($ other ->hasOffsetValueType ($ keyType )->yes ()) {
1696+ return $ other ->getOffsetValueType ($ keyType );
1697+ }
1698+ $ otherUnsealed = $ other ->getUnsealedTypes ();
1699+ if ($ otherUnsealed === null ) {
1700+ return null ;
1701+ }
1702+ [$ unsealedKey , $ unsealedValue ] = $ otherUnsealed ;
1703+ if ($ unsealedKey instanceof NeverType && $ unsealedKey ->isExplicit ()) {
1704+ return null ;
1705+ }
1706+ if ($ unsealedKey ->isSuperTypeOf ($ keyType )->no ()) {
1707+ return null ;
1708+ }
1709+ return $ unsealedValue ;
1710+ };
1711+
1712+ $ keysToProcess = [];
1713+ foreach ($ aKeyByValue as $ keyValue => $ k ) {
1714+ $ keysToProcess [$ keyValue ] = [$ k , $ bKeyByValue [$ keyValue ] ?? null ];
1715+ }
1716+ foreach ($ bKeyByValue as $ keyValue => $ k ) {
1717+ if (!array_key_exists ($ keyValue , $ keysToProcess )) {
1718+ $ keysToProcess [$ keyValue ] = [null , $ k ];
1719+ }
1720+ }
1721+
1722+ foreach ($ keysToProcess as [$ aIdx , $ bIdx ]) {
1723+ if ($ aIdx !== null && $ bIdx !== null ) {
1724+ $ keyType = $ a ->getKeyTypes ()[$ aIdx ];
1725+ $ value = self ::intersect ($ a ->getValueTypes ()[$ aIdx ], $ b ->getValueTypes ()[$ bIdx ]);
1726+ $ optional = $ a ->isOptionalKey ($ aIdx ) && $ b ->isOptionalKey ($ bIdx );
1727+ } elseif ($ aIdx !== null ) {
1728+ $ keyType = $ a ->getKeyTypes ()[$ aIdx ];
1729+ $ aValue = $ a ->getValueTypes ()[$ aIdx ];
1730+ $ bValue = $ resolveOtherValue ($ b , $ keyType );
1731+ if ($ bValue === null ) {
1732+ if ($ a ->isOptionalKey ($ aIdx )) {
1733+ continue ;
1734+ }
1735+ return new NeverType ();
1736+ }
1737+ $ value = self ::intersect ($ aValue , $ bValue );
1738+ $ optional = $ a ->isOptionalKey ($ aIdx );
1739+ } else {
1740+ $ keyType = $ b ->getKeyTypes ()[$ bIdx ];
1741+ $ bValue = $ b ->getValueTypes ()[$ bIdx ];
1742+ $ aValue = $ resolveOtherValue ($ a , $ keyType );
1743+ if ($ aValue === null ) {
1744+ if ($ b ->isOptionalKey ($ bIdx )) {
1745+ continue ;
1746+ }
1747+ return new NeverType ();
1748+ }
1749+ $ value = self ::intersect ($ aValue , $ bValue );
1750+ $ optional = $ b ->isOptionalKey ($ bIdx );
1751+ }
1752+
1753+ if ($ value instanceof NeverType) {
1754+ if ($ optional ) {
1755+ continue ;
1756+ }
1757+ return new NeverType ();
1758+ }
1759+ $ newArray ->setOffsetValueType ($ keyType , $ value , $ optional );
1760+ }
1761+
1762+ return $ newArray ->getArray ();
1763+ }
1764+
16371765 /**
16381766 * Merge two IntersectionTypes that have the same structure but differ
16391767 * in HasOffsetValueType value types (matched by offset key).
0 commit comments