0

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:

  • Does psalm support multiple-type templating as I'm trying to achieve
  • Am I missing the correct semantics in the example above; how should I annotate this?
  • Or is it just a PhpStorm limitation
Dinu
  • 1,374
  • 8
  • 21
  • assuming that class Engine is empty - that's a correct warning. Do you mind adding the Engine and EngineType classes into description? – Dmitrii Jan 05 '21 at 13:19
  • @Dmitrii - sure, added the class structure; EngineType is not a class, it's a type template – Dinu Jan 05 '21 at 13:24
  • with current design I would suggest to temporary disable this inspection. Either in inspection settings or using Alt+Enter on a highlighted member > select inspection quickfix > right arrow > suppress for file. – Dmitrii Jan 05 '21 at 13:35
  • @Dmitrii thanks, but I'm not looking for that; I need both method autocompletion and static type checking to work if at all possible – Dinu Jan 05 '21 at 13:37
  • shouldn't you have `@return DieselEngine` in ::getEngine() in that case? If not - I can't see how any analyzer can parse this statically . – Dmitrii Jan 05 '21 at 14:57
  • @Dmitrii - I think one of us may be missing the point here. Please take a look at https://psalm.dev/docs/annotating_code/templated_annotations/ , these annotations are specifically designed to do just what you don't see possible, that's been around for decades in other languages and that should be supported by PhpStorm, although I've been having a rough time finding what part exactly of the psalm semantics it is supposed to support ATM. So please let's stick to the 3 questions I formulated precisely so that we don't get too philosophical about it. – Dinu Jan 05 '21 at 18:25

1 Answers1

2

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.

weirdan
  • 2,499
  • 23
  • 27
  • Thanks, excellent reply, and it made me laugh, too! I still haven't found out about the PHPStorm capabilities around this, but at least knowing the proper syntax is a good starting point. The palm online sandbox is also a great tool. So I'll warp 9 accept this answer. – Dinu Mar 22 '21 at 00:23