Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How are constructor types inferred? #4

Open
nikic opened this issue Jan 11, 2021 · 2 comments
Open

How are constructor types inferred? #4

nikic opened this issue Jan 11, 2021 · 2 comments

Comments

@nikic
Copy link

nikic commented Jan 11, 2021

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?

@orklah
Copy link

orklah commented Jan 11, 2021

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 a ValueHolder<21>. On the opposite direction, it could be a ValueHolder<scalar> where the first value happen to be a literal int. (this example could make more sense with objects where we push a Child class and the tool has to decide whether it's a ValueHolder<Child> or a ValueHolder<Parent_> with a Child 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> */

@DaveLiddament
Copy link
Owner

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?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants