phptools-docs icon indicating copy to clipboard operation
phptools-docs copied to clipboard

Type detection fails for generic parameter from a method when implementing dependent interfaces

Open evolbug opened this issue 1 year ago • 2 comments

Hi, I found an issue with type detection when:

  • parent interface uses interface-level generic parameter in method signature
  • another child interface extends parent interface, replaying the generic
  • class then implements both interfaces, defining the generic type on both
  • result: method parameter in the class fails type detection and falls back to plain type

Here's a replication:

<?php

interface Thing {}

class Item implements Thing
{
    public int $field = 10;
}

/**
 * @template T of Thing
 */
interface IParent
{
    /**
     * @param T $para
     */
    public function foo(Thing $para);
}

/**
 * @template T of Thing
 * @extends IParent<T>
 */
interface IChild extends IParent {}

/**
 * @implements IParent<Item>
 */
class Bar implements IParent
{
    public function foo(Thing $para): void
    {
        // all good
        $para->field;
    }
}

/**
 * @implements IParent<Item>
 * @implements IChild<Item>
 */
class Foo implements IChild, IParent
{
    public function foo(Thing $para): void
    {
        // Undefined property: Thing::$field
        // Expected: same behaviour as Bar
        $para->field;
    }
}

image

It's an excellent extension otherwise, thanks for the hard work!

evolbug avatar Sep 24 '24 23:09 evolbug

Thank for the code sample! We're happy you like our extension :)

I'll try to make this working in the next update;

jakubmisek avatar Sep 25 '24 13:09 jakubmisek

heya, I discovered that actually it doesn't matter as much on dependent generic interfaces, but maybe on the order of interfaces instead? This appears to replicate order dependence


<?php declare(strict_types = 1);

interface Thing {}

class Item implements Thing
{
    public int $field = 10;
}

/**
 * @template T of Thing
 */
interface IParent
{
    /**
     * @param T $para
     */
    public function foo(Thing $para);
}

interface IChild {}

/**
 * @implements IParent<Item>
 */
class Bar implements IParent, IChild
{
    public function foo(Thing $para): void
    {
        // all good
        $para->field;
    }
}

/**
 * @implements IParent<Item>
 */
class Foo implements IChild, IParent
{
    public function foo(Thing $para): void
    {
        // Undefined property: Thing::$field
        $para->field;
    }
}

evolbug avatar Sep 25 '24 22:09 evolbug

Thank you again.

The recent update fixes that already (the first test case https://github.com/DEVSENSE/phptools-docs/issues/667#issue-2546563927):

image

jakubmisek avatar Dec 17 '24 09:12 jakubmisek