<?php

class Result implements IteratorAggregate {
    public function getIterator() {
        return new ArrayIterator($this);
    }
}

/** @var Result<int> */
$a = new Result();

foreach ($a as $item) {
	\PHPStan\dumpType($item);
}

phpstan understands $item as int, but LSP return Tvalue.

<?php

/** @var iterable<int,string> */
$a = [];
foreach ($a as $item) {
	// $item is string, but LSP return int|string
}

ging-dev thank you! You are right, although the code is not exactly according to PHPStan specifications (which we follow). There should be a @template annotation:

/**
  * @template TValue
  * @implements IteratorAggregate<int, TValue>
  */
class Result ...

Anyways; we'll try to implement the functionality even without the @template specified.

JakubMisek yes, also /** @var Result&\IteratorAggregate<int,string> */ doesn't seem to work either

    ging-dev just noting, @var supposes to only work above variables.

      JakubMisek yes

      <?php
      
      interface Test
      {
          public function some(): string;
      }
      
      class Result {
      }
      
      /** @var Result&\IteratorAggregate<Test> */
      $a = new Result();
      
      foreach ($a as $item) {
      // $item should be Test in this case
      }

      ging-dev I see, it gets confused by Result (which we don't annotate correctly yet) and results in mixed

      The following is a quick workaround before we fix it.

      /** @var \IteratorAggregate<Test> */

        ging-dev Currently, the first case needs two generic arguments, then it works. i.e.:

        /** @var Result<mixed, int> */
        $a = new Result();
        
        foreach ($a as $item) ...

        We'll update the code analysis, to handle the case with one generic argument only.

        The second case with iterable has been fixed; the fix will be in the next update.

          Write a Reply...