Bug report
PHPStan infers the type of new GenericClassName expression based on passed arguments. There are also cases where no initial data is passed, so PHPStan infers the template arguments from the variable type:
/**
* @template T
*/
class Collection {}
class Test {
/** @var Collection<int> */
private readonly Collection $collection;
public function __construct() {
// no error here, even though PHPStan doesn't know what the generic type T is here
$this->collection = new Collection();
}
}
However, if you add an optional constructor parameter of generic type it breaks:
class Collection {
/**
* @param T $t
*/
public function __construct(private readonly mixed $t = null) {}
}
class Test {
/** @var Collection<int> */
private readonly Collection $collection;
public function __construct() {
// error here :(
$this->collection = new Collection(); // Property Test::$collection (Collection<int>) does not accept Collection<null>.
}
}
Code snippet that reproduces the problem
https://phpstan.org/r/74516824-48b7-429c-9fd9-8e58cbbc20dd
https://phpstan.org/r/841b0a05-01e3-4ad1-a916-e5001c037fea
Expected output
PHPStan should always prefer inferring generic types based on the variable type (in this case - @var tag of the property), instead of the passed parameters. So instead of reporting that this value can't be assigned to this property, PHPStan should be reporting that these parameters (if any passed) don't match the inferred expected argument types.
So given this example:
/**
* @template T
*/
class Collection {
/**
* @param array<T> $items
*/
public function __construct(private readonly array $items) {}
}
class Test {
/** @var Collection<int> */
private readonly Collection $collection;
public function __construct() {
$this->collection = new Collection(["string"]);
}
}
the error should be something like Argument #1 of Collection::__construct (int) does not accept string, when it currently reports the opposite: Property Test::$collection (Collection<int>) does not accept Collection<string>..
I'm suggesting a change in inference logic because I'm not sure if this bug can be fixed without this change. This is also how it works in Kotlin's generics - that's why they don't have this problem.
Did PHPStan help you today? Did it make you happy in any way?
It is very nice to have a baseline and finally be able to slowly sort through thousands of little problems, dozen at a time. PHPStan is slowly making all of us happy :)
Bug report
PHPStan infers the type of
new GenericClassNameexpression based on passed arguments. There are also cases where no initial data is passed, so PHPStan infers the template arguments from the variable type:However, if you add an optional constructor parameter of generic type it breaks:
Code snippet that reproduces the problem
https://phpstan.org/r/74516824-48b7-429c-9fd9-8e58cbbc20dd
https://phpstan.org/r/841b0a05-01e3-4ad1-a916-e5001c037fea
Expected output
PHPStan should always prefer inferring generic types based on the variable type (in this case - @var tag of the property), instead of the passed parameters. So instead of reporting that this value can't be assigned to this property, PHPStan should be reporting that these parameters (if any passed) don't match the inferred expected argument types.
So given this example:
the error should be something like
Argument #1 of Collection::__construct (int) does not accept string, when it currently reports the opposite:Property Test::$collection (Collection<int>) does not accept Collection<string>..I'm suggesting a change in inference logic because I'm not sure if this bug can be fixed without this change. This is also how it works in Kotlin's generics - that's why they don't have this problem.
Did PHPStan help you today? Did it make you happy in any way?
It is very nice to have a baseline and finally be able to slowly sort through thousands of little problems, dozen at a time. PHPStan is slowly making all of us happy :)