-
Notifications
You must be signed in to change notification settings - Fork 506
Make ParametersAcceptorSelector::selectSingle() return union-ed conditional types #1159
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
Make ParametersAcceptorSelector::selectSingle() return union-ed conditional types #1159
Conversation
…tional types See phpstan/phpstan/issues/3853#issuecomment-1082351763
|
||
public function getReturnType(): Type | ||
{ | ||
return TypeTraverser::map($this->acceptor->getReturnType(), function (Type $type, callable $traverse) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe cache the result here in a private property? :)
42e5efc
to
a71c977
Compare
$parametersAcceptor = $parametersAcceptors[0]; | ||
if (TypeUtils::containsConditional($parametersAcceptor->getReturnType())) { | ||
return new SingleParametersAcceptor($parametersAcceptor); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I couldn't immediately get tests to pass without guarding here. I could whip up a helper class that can convert a given ParametersAcceptor
into the same type but with the return type adjusted? That way the contract still holds.
public function getReturnType(): Type | ||
{ | ||
if ($this->returnType === null) { | ||
return $this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
style-nit - the line is already long enough, and will return on the outer block immediately with the same value
return $this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) { | |
$this->returnType = TypeTraverser::map($this->acceptor->getReturnType(), static function (Type $type, callable $traverse) { |
assertType('bool', $this->get(0)); | ||
assertType('bool', $this->get(1)); | ||
assertType('bool', $this->get(2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we expect a more precise type here?
assertType('bool', $this->get(0)); | |
assertType('bool', $this->get(1)); | |
assertType('bool', $this->get(2)); | |
assertType('false', $this->get(0)); | |
assertType('true', $this->get(1)); | |
assertType('false', $this->get(2)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No - there is return type extension that just returns ParametersAcceptorSelector::selectSingle()
to verify that it provides the resulting union type instead of the conditional type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this solution, it's better than what I'd come up with :)
src/Reflection/FunctionVariant.php
Outdated
*/ | ||
public function flattenConditionalsInReturnType(): self | ||
{ | ||
$result = clone $this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike clone
, please create new instance explicitly with new self
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, that's disappointing. Given that these classes are marked @api
and the constructor isn't final, constructing an instance of static
seems impossible.
Or would you accept a version that returns $this
if self::class !== static::class
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Give me a moment, I'll think of something 😊
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, these classes should ideally be final
, there's no reason to extend them. I've marked a new todo for 2.0 when the time comes - class has either to be @api
or final (it can be both).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we can mark them @final
in the meantime?
{ | ||
$returnType = $this->getReturnType(); | ||
|
||
$result = clone $this; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dislike clone
, please create new instance explicitly with new self
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is good enough, I'm happy to merge it :)
Thank you! |
See phpstan/phpstan#3853 (comment)