diff --git a/src/Psalm/Internal/Type/TypeExpander.php b/src/Psalm/Internal/Type/TypeExpander.php index 40bbf2d38..ede605aae 100644 --- a/src/Psalm/Internal/Type/TypeExpander.php +++ b/src/Psalm/Internal/Type/TypeExpander.php @@ -519,7 +519,8 @@ class TypeExpander $return_type = $return_type->setTypeParams($type_params); } elseif ($return_type instanceof TKeyedArray) { $properties = $return_type->properties; - foreach ($properties as &$property_type) { + $changed = false; + foreach ($properties as $k => $property_type) { $property_type = self::expandUnion( $codebase, $property_type, @@ -533,9 +534,44 @@ class TypeExpander $expand_templates, $throw_on_unresolvable_constant, ); + if ($property_type !== $properties[$k]) { + $changed = true; + $properties[$k] = $property_type; + } } unset($property_type); - $return_type = $return_type->setProperties($properties); + $fallback_params = $return_type->fallback_params; + if ($return_type->fallback_params) { + foreach ($fallback_params as $k => $property_type) { + $property_type = self::expandUnion( + $codebase, + $property_type, + $self_class, + $static_class_type, + $parent_class, + $evaluate_class_constants, + $evaluate_conditional_types, + $final, + $expand_generic, + $expand_templates, + $throw_on_unresolvable_constant, + ); + if ($property_type !== $fallback_params[$k]) { + $changed = true; + $fallback_params[$k] = $property_type; + } + } + unset($property_type); + } + if ($changed) { + $return_type = new TKeyedArray( + $properties, + $return_type->class_strings, + $fallback_params, + $return_type->is_list, + $return_type->from_docblock + ); + } } if ($return_type instanceof TObjectWithProperties) { diff --git a/src/Psalm/Type/Atomic/TKeyedArray.php b/src/Psalm/Type/Atomic/TKeyedArray.php index 0910c7601..8339cbf4a 100644 --- a/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/src/Psalm/Type/Atomic/TKeyedArray.php @@ -475,11 +475,32 @@ class TKeyedArray extends Atomic ); } - if ($properties === $this->properties) { + $fallback_params = $this->fallback_params; + + foreach ($fallback_params ?? [] as $offset => $property) { + $fallback_params[$offset] = TemplateStandinTypeReplacer::replace( + $property, + $template_result, + $codebase, + $statements_analyzer, + $input_type_param, + $input_arg_offset, + $calling_class, + $calling_function, + $replace, + $add_lower_bound, + null, + $depth + ); + } + + + if ($properties === $this->properties && $fallback_params === $this->fallback_params) { return $this; } $cloned = clone $this; $cloned->properties = $properties; + $cloned->fallback_params = $fallback_params; return $cloned; } @@ -498,7 +519,15 @@ class TKeyedArray extends Atomic $codebase ); } - if ($properties !== $this->properties) { + $fallback_params = $this->fallback_params; + foreach ($fallback_params as $offset => $property) { + $fallback_params[$offset] = TemplateInferredTypeReplacer::replace( + $property, + $template_result, + $codebase + ); + } + if ($properties !== $this->properties || $fallback_params !== $this->fallback_params) { $cloned = clone $this; $cloned->properties = $properties; return $cloned;