Аннотация к псалму для разнотипного шаблона

Мне нужно создать черту (или класс, если на то пошло), на основе которого я могу шаблонировать несколько типов; Я пробовал что-то вроде следующего (также описывающее проблему; контекст автомобиля предназначен только для иллюстрации проблемы, я знаю, что автомобиль должен быть агрегирован, а не составлен, но это не проблема для обсуждения):

      /**
  * @template TyreType of Tyre
  * @template EngineType of Engine
  */
trait Car {
    /**
      * @return TyreType
      */
    public function getTyre(): Tyre {
    }

    /**
      * @return EngineType
      */
    public function getEngine(): Engine{
    }
}

trait SomeCar {
    /**
     * @use Car<AirlessTyre><DieselEngine>
     */
    use Car;

    public function test() {
        $this->getEngine()->dieselSpecificMethod();
    }
}

class Engine{}
class Tyre{}
class DieselEngine extends Engine {
    public function dieselSpecificMethod() {}
}
class AirlessTyre extends Tyre {}

Проблема в том, что в PhpStorm я получаю «Потенциально полиморфный вызов. Engine не имеет элементов в своей иерархии» на dieselSpecificMethod().

Итак, мои вопросы:

  • Поддерживает ли псалом несколько типов шаблонов, поскольку я пытаюсь достичь
  • Не хватает ли мне правильной семантики в приведенном выше примере; как мне это аннотировать?
  • Или это просто ограничение PhpStorm

1 ответ

Psalm поддерживает несколько параметров типа. Правильный синтаксис для их использования: GenericType<TA, TB> ( GenericType<TA><TB>который вы использовали, не распознается). После исправления этой проблемы (и еще нескольких подавленных, чтобы избавиться от ненужного шума) это становится:

      <?php
/**
  * @template TyreType of Tyre
  * @template EngineType of Engine
  */
trait Car {
    /**
      * @return TyreType
      * @psalm-suppress InvalidReturnType
      */
    public function getTyre(): Tyre {
    }

    /**
      * @return EngineType
      * @psalm-suppress InvalidReturnType
      */
    public function getEngine(): Engine{
    }
}

trait SomeCar {
    /**
     * @use Car<AirlessTyre,DieselEngine>
     */
    use Car;

    public function test():void {
        $this->getEngine()->dieselSpecificMethod();
        $this->getEngine()->warp(9);
    }
}

class FordCar { use SomeCar; }

class Engine{}
class Tyre{}
class DieselEngine extends Engine {
    public function dieselSpecificMethod():void {}
}
class WarpEngine extends Engine {
    public function warp(int $speed): void {}
}
class AirlessTyre extends Tyre {}

Псалом может сказать вам, что у Ford определенно нет возможностей двигателя деформации: https://psalm.dev/r/31343aafc3 . Обратите внимание на правильный синтаксис для ссылки на общую черту в @use аннотация.

Другие вопросы по тегам