Меня тут осенило, что тип static в PHP (доступен с версии 8.0) — это, по сути, дженерик. В каждом классе его можно представить как ковариантный параметр-тип с ограничением
self, который прокидывается по цепочке наследования, каждый раз уточняясь.
/**
* @template-covariant TStatic of A
*/
abstract class A
{
/**
* @return TStatic
*/
abstract public function returnStatic(): object;
}
/**
* @template-covariant TStatic of B
* @extends A<TStatic>
*/
abstract class B extends A
{
}
Пруф в песочнице Psalm вместо тысячи слов: https://psalm.dev/r/a5eda433d5