-
Notifications
You must be signed in to change notification settings - Fork 11.1k
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
[11.x] feat: refine return type for throw_if
and throw_unless
to reflect actual behavior with "falsey" values
#53154
Conversation
throw_if
and throw_unless
to reflect actual behavior with "non-falsey" valuesthrow_if
and throw_unless
to reflect actual behavior with "falsey" values
The entire point of the referenced PR was to narrow the type after the function call, this PR removed support for narrowing the types and had to rewrite the tests because they were failing. This now fails: throw_unless(is_int($foo));
assertType('int', $foo); |
@calebdw Apologies, the loss of type narrowing was unintentional. Trying to achieve both type-narrowing and compatibility with falsey values, I got this annotation for * @return ($condition is true ? never : ($condition is non-empty-mixed ? never : TValue)) But I'm struggling to find a similar annotation for |
I would assume something like this would work: @return ($condition is true ? TValue : ($condition is non-empty-mixed ? TValue : never)) |
That version gives:
This annotation seems to work: * @return ($condition is false ? never : ($condition is non-empty-mixed ? TValue : never)) If you agree, I will open a new pull request with changed annotations and your original type-narrowing tests. |
Ah, yeah that makes sense. I'm fine with these changes if the tests I originally had continue to pass. Also, you added a bunch of assertions on the return type, but you can also add some tests to see if your falsy stuff is narrowed: /** @var Model|null $model */
throw_unless($model);
assertType('Model', $model); |
@calebdw Good call. I can't get type-narrowing to work for function testThrowUnlessWithFalseyValue(Carbon\Carbon|null $date): void
{
throw_unless($date);
assertType('Carbon', $date); // → ⚠️ Expected type Carbon, actual: Carbon\Carbon|null
} @calebdw Can you tell what I'm missing? |
Maybe try using * @return ($condition is false ? never : ($condition is empty ? never : TValue)) However, I'm not the most concerned with supporting this case---as long as the strict function test(Carbon\Carbon|null $date): void
{
throw_if($date === null);
throw_if(is_null($date));
throw_unless($date !== null);
throw_unless(! is_null($date));
throw_unless($date instanceof Carbon);
// etc.
assertType('Carbon', $date);
} |
…reflect actual behavior with "falsey" values (laravel#53154) * let return type reflect actual behavior with falsey values * let type assertions reflect actual behavior with falsey values
Since this was merged PhpStorm (storm, not stan) has started reporting every statement following As far as I'm aware I have everything set up correctly. Phpstan is enabled and configured in PhpStorm, it's not detecting any errors, I'm up to date on everything... It just seems like, for some reason, PhpStorm isn't accepting the conditional return types. Supposedly it can, but I don't know if it's a bug, or something I'm doing wrong. Anyone else, or is it something at my end? Thanks! 🙏 |
It might be having issues with the |
It looks like it's a PhpStorm bug after all 🤦♂️ WI-78351 Type not correctly inferred from phpstan/psalm conditional generic return type |
In #53005, the return types for
throw_if
andthrow_unless
were narrowed to specifyTValue
ornever
depending on$condition
being exactlytrue
or not:This doesn't reflect the actual behavior of
throw_if
andthrow_unless
: Both check for "falseyness", and not strict equality totrue
.Crucially, the new type annotation gets in the way when e.g.
throw_unless
is used to check for the presence of something, e.g.:To keep phpstan happy, developers would have to rewrite this to e.g.:
Luckily, phpstan offers the
non-empty-mixed
"advanced type", allowing us to precisely specify the behavior ofthrow_if
andthrow_unless
: