Skip to content

Possible type loss when binding variables with 'use' in anonymous functions #12875

@jnoordsij

Description

@jnoordsij

Bug report

When passing variables with types narrowed using conditionals to an anonymous function with the use keyword, there seems to be some loss of type information, making the type inside the function more broad than outside the scope.

This does not happen with arrow functions, which likely are treated differently, given how they also bind variables in a different manner.

Code snippet that reproduces the problem

https://phpstan.org/r/73f07010-4dee-4c3c-ac35-7dc5a29c6e9a

<?php declare(strict_types = 1);

interface HasFoo
{
    public function foo(): int;
}

interface HasBar
{
    public function bar(): int;
}

class HelloWorld
{
    /**
     * @param  "foo"|"bar"  $method
     * @param  ($method is "foo" ? HasFoo : HasBar)  $a
     * @param  ($method is "foo" ? HasFoo : HasBar)  $b
     */    
    public function add(string $method, HasFoo|HasBar $a, HasFoo|HasBar $b): void
    {
        $add = $a->{$method}() + $b->{$method}();
        $addInArrow = fn () => $a->{$method}() + $b->{$method}();
        $addInAnonymous = function () use ($a, $b, $method): int {
            return $a->{$method}() + $b->{$method}(); // this suddenly no longer knows about the relation between $method and $a/$b types
        };
    }
}

Expected output

All three cases to be considered equivalent and not reporting any errors.

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

With the new features of PHPStan and the hard work on the Laravel side, more and more of my code gets typechecked, which greatly helps in preventing me from making simple mistakes while coding!

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions