php-generics-standard
php-generics-standard copied to clipboard
How are constructor types inferred?
The description in https://github.com/DaveLiddament/php-generics-standard#constructor is a bit unclear to me. If you are passing a literal integer value, it's obvious that T = int
, but what if the value is dynamic?
function test(?int $foo) {
$valueHolder = new ValueHolder($foo);
}
In this case, $foo
can be int
or null
at runtime. However, I would assume that the actual inferred type is T = ?int
. Is that correct?
If it is, then what about this variant?
function test(?int $foo) {
if (null !== $foo) {
$valueHolder = new ValueHolder($foo);
}
}
Here, $foo
is known to be int
, but only through flow-sensitive type analysis. Would T = int
here?
Well, from a static analysis point of view, there is two possibilities:
- The analysis tool try to infer the type of
T
from the value. This is delicate because there is a granularity of type to consider. For example,new ValueHolder(21);
could very well be aValueHolder<21>
. On the opposite direction, it could be aValueHolder<scalar>
where the first value happen to be a literal int. (this example could make more sense with objects where we push aChild
class and the tool has to decide whether it's aValueHolder<Child>
or aValueHolder<Parent_>
with aChild
as first element) - The analysis tool can require the user to document the type of the templated class. This is the choice made by psalm and phpstan if I'm not mistaken. Psalm will consider
T = empty
in the absence of an explicit/** @var ValueHolder<int> */
Great question. My initial response is that the type must be explicitly stated:
e.g.
/** @var ValueHolder<int> $intHolder */
$intHolder = new ValueHolder($value) ;
This maps to what would happen in Java (based on my Java knowledge from 5 years ago!) ...
intHolder = new ValueHolder<int>();
@orklah raises an interesting point when inferring the type. Is new ValueHolder(new Dog());
holding Dog
or Animal
. Psalm assumes Dog, which I tend to agree with.
Either way the ambiguity suggests adds weight the argument that the type should be explicitly stated.
Thoughts?