-
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: narrow types for throw_if and throw_unless #53005
Conversation
Thank you sir! |
@calebdw, is there any way for this to check for truthy values rather than explicitly
$model = Model::first();
throw_unless($model); See: https://phpstan.org/r/ee1060c6-333b-4d06-9362-17b69c691d28 All good if not. The experience hasn't changed for that use-case and has improved for others. Just wondered if it was possible. |
I don't think so, not without creating a custom PHPStan extension (which seems overkill). The issue is that PHPStan doesn't really have a "truthy" type (as far as I am aware). I tried reversing the condition and using While your code is valid, all you need to do is slightly adjust the expression for better type narrowing: $model = Model::first();
throw_if($model === null);
throw_if(is_null($model));
// etc.
assertType('Model', $model); |
@calebdw could explicitly using |
Source: phpstan/phpstan#11335 (comment) These annotations reflect how if (! function_exists('throw_if')) {
/**
* Throw the given exception if the given condition is true.
*
* @template TValue
* @template TException of \Throwable
*
* @param TValue $condition
* @param TException|class-string<TException>|string $exception
* @param mixed ...$parameters
* @return ($condition is true ? never : ($condition is false ? TValue : ($condition is null ? TValue : ($condition is array{} ? TValue : ($condition is "0" ? TValue : ($condition is "" ? TValue : ($condition is 0 ? TValue : ($condition is 0.0 ? TValue : never))))))))
*
* @throws TException
*/
function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
{
if ($condition) {
if (is_string($exception) && class_exists($exception)) {
$exception = new $exception(...$parameters);
}
throw is_string($exception) ? new RuntimeException($exception) : $exception;
}
return $condition;
}
}
if (! function_exists('throw_unless')) {
/**
* Throw the given exception unless the given condition is true.
*
* @template TValue
* @template TException of \Throwable
*
* @param TValue $condition
* @param TException|class-string<TException>|string $exception
* @param mixed ...$parameters
* @return ($condition is true ? TValue : ($condition is false ? never : ($condition is null ? never : ($condition is array{} ? never : ($condition is "0" ? never : ($condition is "" ? never : ($condition is 0 ? never : ($condition is 0.0 ? never : TValue))))))))
*
* @throws TException
*/
function throw_unless($condition, $exception = 'RuntimeException', ...$parameters)
{
throw_if(! $condition, $exception, ...$parameters);
return $condition;
}
} Not particularly pretty through. |
@crishoj, that seems more complicated than it's worth---particularly given that you can likely update the code to have a boolean expression that will narrow the type |
@calebdw With the new changes 46dc7a4 here phpstan now throws When I modify the
I think this will affect a lot of people. To add a type conversion just for this is overkill. EDIT: sorry the |
@gazben thanks for drawing attention to the "advanced" phpstan type (Added in phpstan/phpstan-src@387ebd5) Given this, I would suggest changing the return type signatures to the following: /**
* Throw the given exception if the given condition is true.
*
* @template TValue
* @template TException of \Throwable
*
* @param TValue $condition
* @param TException|class-string<TException>|string $exception
* @param mixed ...$parameters
* @return ($condition is non-empty-mixed ? never : TValue)
*
* @throws TException
*/
function throw_if($condition, $exception = 'RuntimeException', ...$parameters)
{
// ...
}
/**
* Throw the given exception unless the given condition is true.
*
* @template TValue
* @template TException of \Throwable
*
* @param TValue $condition
* @param TException|class-string<TException>|string $exception
* @param mixed ...$parameters
* @return ($condition is non-empty-mixed ? TValue : never)
*
* @throws TException
*/
function throw_unless($condition, $exception = 'RuntimeException', ...$parameters)
{
// ...
} It works well in my end. |
@crishoj, I see you submitted a PR---you had to change the tests in order to use This now fails: throw_unless(is_int($foo));
assertType('int', $foo); |
@calebdw Apologies, the loss of type-narrowing was unintentional. Do you think we could adjust the type annotation so that both type narrowing and actual behavior with "falsey" values is achieved? |
You might could use something like the following (where @return ($condition is true ? never : ($condition is non-empty-mixed ? never : TValue)) However, I still think this is more trouble than it's worth, more strict levels of phpstan require that a boolean expression is passed to an |
Hello!
This PR correctly causes the types to be narrowed for the
throw_{if_unless}
functions.Thanks!