Skip to content

Inconsistent error on generics #11041

Closed
Closed

Description

@tamiroh

Bug report

For the following code with generics,

<?php declare(strict_types = 1);

/**
 * @template TKey
 * @template TValue
 */
class Collection
{
    ...
}

/**
  * @param Collection<int, string|null> $collection
  */
function testFunc(Collection $collection): void
{

}

$collection = new Collection([0 => 'foo', 1 => 'bar', 2 => null, 3 => 'baz']);

PHPStan reports the following inconsistent errors.

Parameter #1 $collection of function testFunc expects Collection<int, string|null>, Collection<int, string|null> given.

From my few attempts, this seems to happen when...

  • The value of the collection is nullable
  • Bleeding edge is enabled
  • PHPStan Level is less than 8

Code snippet that reproduces the problem

https://phpstan.org/r/6cb19ed5-8774-4178-a21b-a7e19d1934f9

Expected output

No errors.

Did PHPStan help you today? Did it make you happy in any way?

Yes, really helpful for finding bugs. Thank you so much! 😸

Activity

  1. added this to the Generics milestone on May 16, 2024
  2. tamiroh commented on Sep 3, 2024

    @tamiroh
    Author

    Having attempted somewhat, it seems that setting featureToggles.newRuleLevelHelper to false works around this problem for now 👀

  3. ondrejmirtes commented on Sep 3, 2024

    @ondrejmirtes
    Member

    @tamiroh That's not a solution. This option will go away in 2.0 and everybody will get the same (new) behaviour.

  4. tamiroh commented on Sep 3, 2024

    @tamiroh
    Author

    @ondrejmirtes Yes, you are right. This is just a temporary work-around 😓. So I was just starting to think about how to fix it.

  5. realjjaveweb commented on May 5, 2025

    @realjjaveweb

    I must confirm this still happens, I, very unwillingly, bypassed it by removing the |null from the type, which shouldn't be needed. and it's inconsistent with the App class definition.

    The App class defines @template TContainerInterface of (ContainerInterface|null),
    now normally @param App<ContainerInterface|null> $app works, but not when you call function within the same class that has the same annotation as input:

    <?php
    class MyClass {
        /**
         * @param App<ContainerInterface|null> $app
         */
        public function registerSomething(App $app): void
        {
                $this->doRegister(
                    app: $app,
                    target: 'something',
                );
        }
    
        /**
         * @param App<ContainerInterface|null> $app
         */
        private function doRegister(App $app, string $target): void
        {
            $app->magic($target);
        }
    }
    

    I even tried to define a union type TContainerInterface on the class level as the App class does it and use App<TContainerInterface> - no still the same problem on the line calling ->doRegister(...:

    Parameter $app of method MyClass<TContainerInterface of Psr\Container\ContainerInterface|null>::doRegister() expects
    App<Psr\Container\ContainerInterface|null>, App<Psr\Container\ContainerInterface|null> given.

    Note that I have the latest phpstan as of now - "2.1.14".

    I mean the types are FQNs and EXACTLY the same.

  6. VincentLanglet commented on Aug 2, 2025

    @VincentLanglet
    Contributor

    This is the same issue than #9096 (comment)

  7. phpstan-bot commented on Sep 10, 2026

    @phpstan-bot
    Contributor

    Closed via merging phpstan/phpstan-src#6392

  8. github-actions commented on Oct 12, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

  9. locked as resolved and limited conversation to collaborators on Oct 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions