I need to build a trait (or class for that matter) on which I can template multiple types; I've tried something like the following (also descriptive of the problem; the car context is just for illustrating the problem, I know a car is supposed to be aggregated not composed but this is not the issue to discuss):
/**
* @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 {}
The problem is, in PhpStorm I get "Potentially polymorphic call. Engine does not have members in its hierarchy" on dieselSpecificMethod()
.
So my questions are:
Psalm does support multiple type parameters. The correct syntax to use them is GenericType<TA, TB>
(GenericType<TA><TB>
that you used is not recognized). With that problem fixed (and a couple of more suppressed, to get rid of unnecessary noise) this becomes:
<?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 {}
Psalm can tell you Ford certainly doesn't have warp engine capabilities: https://psalm.dev/r/31343aafc3. Note the correct syntax to reference generic trait in @use
annotation.