You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Type inferrence broken for new ClassName() with a nullable generic argument #8441
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> */privatereadonlyCollection$collection;
publicfunction__construct() {
// no error here, even though PHPStan doesn't know what the generic type T is here$this->collection = newCollection();
}
}
However, if you add an optional constructor parameter of generic type it breaks:
class Collection {
/** * @param T $t */publicfunction__construct(privatereadonlymixed$t = null) {}
}
class Test {
/** @var Collection<int> */privatereadonlyCollection$collection;
publicfunction__construct() {
// error here :($this->collection = newCollection(); // Property Test::$collection (Collection<int>) does not accept Collection<null>.
}
}
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 */publicfunction__construct(privatereadonlyarray$items) {}
}
class Test {
/** @var Collection<int> */privatereadonlyCollection$collection;
publicfunction__construct() {
$this->collection = newCollection(["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 :)
/** @template T */class Collection {
/** * @param T $t */publicfunction__construct(privatereadonlymixed$t = null) {}
}
class Test {
/** @var Collection<int> */privatereadonlyCollection$collection;
publicfunction__construct() {
// error here :($this->collection = newCollection(); // Property Test::$collection (Collection<int>) does not accept Collection<null>.
}
}
And that errors "Argument #1 of Collection::__construct (int) does not accept string" vs. "Property Test::$collection (Collection) does not accept Collection." are roughly equivalent and it actually doesn't matter which one is reported, as long as at least one of them is reported.
Activity
ondrejmirtes commentedon Sep 1, 2026
Hi, I feel like this error is expected:
And that errors "Argument #1 of Collection::__construct (int) does not accept string" vs. "Property Test::$collection (Collection) does not accept Collection." are roughly equivalent and it actually doesn't matter which one is reported, as long as at least one of them is reported.
Please watch this PR phpstan/phpstan-src#6332, it improves a lot in this area.
github-actions commentedon Oct 3, 2026
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.