psalm
psalm copied to clipboard
`self` and `static` return types don't work if parent class has a constrained template type
Consider the following code: https://psalm.dev/r/c676544a45
It works as expected: The parent class get
method returns static
– therefore, when called on the child, we can be sure that the self
return type is correct.
Now, let's constrain the template parameter on the parent class (but in a way where the child is still valid). Urgh! https://psalm.dev/r/6f35cf8914
Now Psalm complains that "The type 'ParentClass<object>' is more general than the declared return type 'ChildClass' for ChildClass::myGet
".
We can't fix that by using static
on the child return type: https://psalm.dev/r/091f9485a7
Seems like we can't fix it at all in our application code, since Psalm seems to override the type constraint over the actual known type of the template.
I found these snippets:
https://psalm.dev/r/c676544a45
<?php
class A {}
/**
* @template T
*/
class ParentClass
{
public function get(): static
{
return $this;
}
}
/**
* @template-extends ParentClass<A>
*/
final class ChildClass extends ParentClass
{
public function myGet(): self
{
return $this->get();
}
}
Psalm output (using commit a75d26a):
No issues!
https://psalm.dev/r/6f35cf8914
<?php
class A {}
/**
* @template T of object
*/
class ParentClass
{
public function get(): static
{
return $this;
}
}
/**
* @template-extends ParentClass<A>
*/
final class ChildClass extends ParentClass
{
public function myGet(): self
{
return $this->get();
}
}
Psalm output (using commit a75d26a):
INFO: LessSpecificReturnStatement - 23:13 - The type 'ParentClass<object>' is more general than the declared return type 'ChildClass' for ChildClass::myGet
INFO: MoreSpecificReturnType - 21:27 - The declared return type 'ChildClass' for ChildClass::myGet is more specific than the inferred return type 'ParentClass<object>'
https://psalm.dev/r/091f9485a7
<?php
class A {}
/**
* @template T of object
*/
class ParentClass
{
public function get(): static
{
return $this;
}
}
/**
* @template-extends ParentClass<A>
*/
final class ChildClass extends ParentClass
{
public function myGet(): static
{
return $this->get();
}
}
Psalm output (using commit a75d26a):
INFO: LessSpecificReturnStatement - 23:13 - The type 'ParentClass<object>' is more general than the declared return type 'ChildClass' for ChildClass::myGet
INFO: MoreSpecificReturnType - 21:27 - The declared return type 'ChildClass' for ChildClass::myGet is more specific than the inferred return type 'ParentClass<object>'
The fix seems to be rather simple: https://psalm.dev/r/872aced924
I found these snippets:
https://psalm.dev/r/872aced924
<?php
class A {}
/**
* @template T of object
*/
class ParentClass
{
/** @return static<T> */
public function get(): static
{
return $this;
}
}
/**
* @template-extends ParentClass<A>
*/
final class ChildClass extends ParentClass
{
public function myGet(): self
{
return $this->get();
}
}
Psalm output (using commit b940c7e):
No issues!
Thanks, @weirdan! I didn't think of a solution like this, because in my case, the parent class lives in a library.
Do you think Psalm should change its behaviour in this regard? After all, the resolved static
type doesn't even support template types, so static<T>
doesn't make so much sense. Otherwise, feel free to close this issue.