@@ -704,24 +704,7 @@ static function (string $variance): TemplateTypeVariance {
704704 if (count ($ genericTypes ) === 1 ) { // array<ValueType>
705705 $ arrayType = new ArrayType ((new BenevolentUnionType ([new IntegerType (), new StringType ()]))->toArrayKey (), $ genericTypes [0 ]);
706706 } elseif (count ($ genericTypes ) === 2 ) { // array<KeyType, ValueType>
707- $ originalKey = $ genericTypes [0 ];
708- if ($ this ->reportUnsafeArrayStringKeyCasting === ReportUnsafeArrayStringKeyCastingToggle::PREVENT ) {
709- $ originalKey = TypeTraverser::map ($ originalKey , static function (Type $ type , callable $ traverse ) {
710- if ($ type instanceof UnionType || $ type instanceof IntersectionType) {
711- return $ traverse ($ type );
712- }
713-
714- if ($ type instanceof StringType) {
715- return TypeCombinator::intersect ($ type , new AccessoryDecimalIntegerStringType (inverse: true ));
716- }
717-
718- return $ type ;
719- });
720- }
721- $ keyType = TypeCombinator::intersect ($ originalKey ->toArrayKey (), new UnionType ([
722- new IntegerType (),
723- new StringType (),
724- ]))->toArrayKey ();
707+ $ keyType = $ this ->transformUnsafeArrayKey ($ genericTypes [0 ]);
725708 $ finiteTypes = $ keyType ->getFiniteTypes ();
726709 if (
727710 count ($ finiteTypes ) === 1
@@ -1001,6 +984,28 @@ static function (string $variance): TemplateTypeVariance {
1001984 return new ErrorType ();
1002985 }
1003986
987+ private function transformUnsafeArrayKey (Type $ keyType ): Type
988+ {
989+ if ($ this ->reportUnsafeArrayStringKeyCasting === ReportUnsafeArrayStringKeyCastingToggle::PREVENT ) {
990+ $ keyType = TypeTraverser::map ($ keyType , static function (Type $ type , callable $ traverse ) {
991+ if ($ type instanceof UnionType || $ type instanceof IntersectionType) {
992+ return $ traverse ($ type );
993+ }
994+
995+ if ($ type instanceof StringType) {
996+ return TypeCombinator::intersect ($ type , new AccessoryDecimalIntegerStringType (inverse: true ));
997+ }
998+
999+ return $ type ;
1000+ });
1001+ }
1002+
1003+ return TypeCombinator::intersect ($ keyType ->toArrayKey (), new UnionType ([
1004+ new IntegerType (),
1005+ new StringType (),
1006+ ]))->toArrayKey ();
1007+ }
1008+
10041009 private function resolveCallableTypeNode (CallableTypeNode $ typeNode , NameScope $ nameScope ): Type
10051010 {
10061011 $ templateTags = [];
@@ -1100,13 +1105,48 @@ private function resolveArrayShapeNode(ArrayShapeNode $typeNode, NameScope $name
11001105 $ builder ->setOffsetValueType ($ offsetType , $ this ->resolve ($ itemNode ->valueType , $ nameScope ), $ itemNode ->optional );
11011106 }
11021107
1108+ $ isList = in_array ($ typeNode ->kind , [
1109+ ArrayShapeNode::KIND_LIST ,
1110+ ArrayShapeNode::KIND_NON_EMPTY_LIST ,
1111+ ], true );
1112+
1113+ if (!$ typeNode ->sealed ) {
1114+ if ($ typeNode ->unsealedType === null ) {
1115+ if ($ isList ) {
1116+ $ unsealedKeyType = IntegerRangeType::createAllGreaterThanOrEqualTo (0 );
1117+ } else {
1118+ $ unsealedKeyType = (new BenevolentUnionType ([new IntegerType (), new StringType ()]))->toArrayKey ();
1119+ }
1120+ $ builder ->makeUnsealed (
1121+ $ unsealedKeyType ,
1122+ new MixedType (),
1123+ );
1124+ } else {
1125+ if ($ typeNode ->unsealedType ->keyType === null ) {
1126+ if ($ isList ) {
1127+ $ unsealedKeyType = IntegerRangeType::createAllGreaterThanOrEqualTo (0 );
1128+ } else {
1129+ $ unsealedKeyType = (new BenevolentUnionType ([new IntegerType (), new StringType ()]))->toArrayKey ();
1130+ }
1131+ } else {
1132+ $ unsealedKeyType = $ this ->transformUnsafeArrayKey ($ this ->resolve ($ typeNode ->unsealedType ->keyType , $ nameScope ));
1133+ }
1134+ $ unsealedKeyFiniteTypes = $ unsealedKeyType ->getFiniteTypes ();
1135+ $ unsealedValueType = $ this ->resolve ($ typeNode ->unsealedType ->valueType , $ nameScope );
1136+ if (count ($ unsealedKeyFiniteTypes ) > 0 ) {
1137+ foreach ($ unsealedKeyFiniteTypes as $ unsealedKeyFiniteType ) {
1138+ $ builder ->setOffsetValueType ($ unsealedKeyFiniteType , $ unsealedValueType , true );
1139+ }
1140+ } else {
1141+ $ builder ->makeUnsealed ($ unsealedKeyType , $ unsealedValueType );
1142+ }
1143+ }
1144+ }
1145+
11031146 $ arrayType = $ builder ->getArray ();
11041147
11051148 $ accessories = [];
1106- if (in_array ($ typeNode ->kind , [
1107- ArrayShapeNode::KIND_LIST ,
1108- ArrayShapeNode::KIND_NON_EMPTY_LIST ,
1109- ], true )) {
1149+ if ($ isList ) {
11101150 $ accessories [] = new AccessoryArrayListType ();
11111151 }
11121152
0 commit comments