@@ -680,11 +680,27 @@ public function isIterableAtLeastOnce(): TrinaryLogic
680680
681681 public function getIterableKeyType (): Type
682682 {
683- $ classReflection = $ this ->getClassReflection ();
684- if ($ classReflection === null ) {
685- return new ErrorType ();
683+ $ isTraversable = false ;
684+ if ($ this ->isInstanceOf (Traversable::class)->yes ()) {
685+ $ isTraversable = true ;
686+ $ tKey = GenericTypeVariableResolver::getType ($ this , Traversable::class, 'TKey ' );
687+ if ($ tKey !== null ) {
688+ if (!$ tKey instanceof MixedType || $ tKey ->isExplicitMixed ()) {
689+ $ classReflection = $ this ->getClassReflection ();
690+ if ($ classReflection === null ) {
691+ return $ tKey ;
692+ }
693+
694+ return TypeTraverser::map ($ tKey , static function (Type $ type , callable $ traverse ) use ($ classReflection ): Type {
695+ if ($ type instanceof StaticType) {
696+ return $ type ->changeBaseClass ($ classReflection )->getStaticObjectType ();
697+ }
698+
699+ return $ traverse ($ type );
700+ });
701+ }
702+ }
686703 }
687-
688704 if ($ this ->isInstanceOf (Iterator::class)->yes ()) {
689705 return RecursionGuard::run ($ this , fn (): Type => ParametersAcceptorSelector::selectSingle (
690706 $ this ->getMethod ('key ' , new OutOfClassScope ())->getVariants (),
@@ -695,17 +711,13 @@ public function getIterableKeyType(): Type
695711 $ keyType = RecursionGuard::run ($ this , fn (): Type => ParametersAcceptorSelector::selectSingle (
696712 $ this ->getMethod ('getIterator ' , new OutOfClassScope ())->getVariants (),
697713 )->getReturnType ()->getIterableKeyType ());
714+ $ isTraversable = true ;
698715 if (!$ keyType instanceof MixedType || $ keyType ->isExplicitMixed ()) {
699716 return $ keyType ;
700717 }
701718 }
702719
703- if ($ this ->isInstanceOf (Traversable::class)->yes ()) {
704- $ tKey = GenericTypeVariableResolver::getType ($ this , Traversable::class, 'TKey ' );
705- if ($ tKey !== null ) {
706- return $ tKey ;
707- }
708-
720+ if ($ isTraversable ) {
709721 return new MixedType ();
710722 }
711723
@@ -714,6 +726,28 @@ public function getIterableKeyType(): Type
714726
715727 public function getIterableValueType (): Type
716728 {
729+ $ isTraversable = false ;
730+ if ($ this ->isInstanceOf (Traversable::class)->yes ()) {
731+ $ isTraversable = true ;
732+ $ tValue = GenericTypeVariableResolver::getType ($ this , Traversable::class, 'TValue ' );
733+ if ($ tValue !== null ) {
734+ if (!$ tValue instanceof MixedType || $ tValue ->isExplicitMixed ()) {
735+ $ classReflection = $ this ->getClassReflection ();
736+ if ($ classReflection === null ) {
737+ return $ tValue ;
738+ }
739+
740+ return TypeTraverser::map ($ tValue , static function (Type $ type , callable $ traverse ) use ($ classReflection ): Type {
741+ if ($ type instanceof StaticType) {
742+ return $ type ->changeBaseClass ($ classReflection )->getStaticObjectType ();
743+ }
744+
745+ return $ traverse ($ type );
746+ });
747+ }
748+ }
749+ }
750+
717751 if ($ this ->isInstanceOf (Iterator::class)->yes ()) {
718752 return RecursionGuard::run ($ this , fn (): Type => ParametersAcceptorSelector::selectSingle (
719753 $ this ->getMethod ('current ' , new OutOfClassScope ())->getVariants (),
@@ -724,17 +758,13 @@ public function getIterableValueType(): Type
724758 $ valueType = RecursionGuard::run ($ this , fn (): Type => ParametersAcceptorSelector::selectSingle (
725759 $ this ->getMethod ('getIterator ' , new OutOfClassScope ())->getVariants (),
726760 )->getReturnType ()->getIterableValueType ());
761+ $ isTraversable = true ;
727762 if (!$ valueType instanceof MixedType || $ valueType ->isExplicitMixed ()) {
728763 return $ valueType ;
729764 }
730765 }
731766
732- if ($ this ->isInstanceOf (Traversable::class)->yes ()) {
733- $ tValue = GenericTypeVariableResolver::getType ($ this , Traversable::class, 'TValue ' );
734- if ($ tValue !== null ) {
735- return $ tValue ;
736- }
737-
767+ if ($ isTraversable ) {
738768 return new MixedType ();
739769 }
740770
0 commit comments