diff --git a/src/Psalm/Internal/Analyzer/MethodComparator.php b/src/Psalm/Internal/Analyzer/MethodComparator.php index 9b07d8fe2a1..307aee3e3f0 100644 --- a/src/Psalm/Internal/Analyzer/MethodComparator.php +++ b/src/Psalm/Internal/Analyzer/MethodComparator.php @@ -589,6 +589,18 @@ private static function compareMethodDocblockParams( } } + foreach ($guide_method_storage_param_type->getAtomicTypes() as $k => $t) { + if ($t instanceof Type\Atomic\TTemplateParam + && \strpos($t->defining_class, 'fn-') === 0 + ) { + $guide_method_storage_param_type->removeType($k); + + foreach ($t->as->getAtomicTypes() as $as_t) { + $guide_method_storage_param_type->addType($as_t); + } + } + } + $union_comparison_results = new TypeComparisonResult(); if (!TypeAnalyzer::isContainedBy( diff --git a/tests/Template/ConditionalReturnTypeTest.php b/tests/Template/ConditionalReturnTypeTest.php index 395c1ec8b9e..e3f735a7ffc 100644 --- a/tests/Template/ConditionalReturnTypeTest.php +++ b/tests/Template/ConditionalReturnTypeTest.php @@ -549,6 +549,45 @@ function bar(SerializerInterface $i, string $data): array { return $i->deserialize($data, \'array\'); }' ], + 'inheritConditional' => [ + ' + * @param T2 $type + * @return (T2 is "int" ? static : static) + */ + public static function ofType(string $type); + } + + + /** + * @template E + * @implements AInterface + */ + class BClass implements AInterface { + protected string $type; + + protected function __construct(string $type) + { + $this->type = $type; + } + + /** + * @template T + * @template T2 as "int"|class-string + * @param T2 $type + * @return (T2 is "int" ? static : static) + */ + public static function ofType(string $type) { + return new static($type); + } + }' + ], ]; } }