This is partially related to the https://github.com/vimeo/psalm/issues/3443 but mostly is a feature request/suggestion/question of its own.
At the moment to express "not empty string" type one would use the following annotations
/**
* @psalm-pure
* @psalm-assert string $value
* @psalm-assert !empty $value
*
* @param mixed $value
* @param string $message
*
* @throws InvalidArgumentException
*/
public static function stringNotEmpty($value, $message = '')
{
static::string($value, $message);
static::notEq($value, '', $message);
}
Now if we want to make this assertion nullable so that it expressed as "null or not empty string" then we realise it's not really possible to encode it in type.
If I'm not wrong in everything I wrote above, then the question would be: how feasible instead of using 2 @psalm-assert - to support a string & !empty type annotation?
Then the desired nullable option for it would be null | (string & !empty).
Also relevant:
/**
* @psalm-pure
* @psalm-template UnexpectedType of object
* @psalm-param class-string<UnexpectedType> $class
* @psalm-assert !UnexpectedType $value
* @psalm-assert !class-string<UnexpectedType> $value
*
* @param object|string $value
* @param string $class
* @param string $message
*
* @throws InvalidArgumentException
*/
public static function isNotA($value, $class, $message = '') {}
Your specific case is possible with non-empty-string type: https://psalm.dev/r/f1537fc1b1 though
Also relevant:
That works: https://psalm.dev/r/b8afb83865
I found these snippets:
https://psalm.dev/r/f1537fc1b1
<?php
class Assert {
/**
* @psalm-pure
* @psalm-assert non-empty-string $value
*
* @param mixed $value
* @param string $message
*
* @throws InvalidArgumentException
*/
public static function stringNotEmpty($value, $message = ''): void
{
assert(is_string($value), $message);
assert($value !== '', $message);
}
/**
* @psalm-pure
* @psalm-assert null|non-empty-string $value
*
* @param mixed $value
* @param string $message
*
* @throws InvalidArgumentException
*/
public static function nullOrNotEmptyString($value, $message = ''): void {
if ($value === null) return;
static::stringNotEmpty($value, $message);
}
}
$s = rand(0,1) ? null : substr("a", 0, 1);
Assert::nullOrNotEmptyString($s);
/** @psalm-trace $s */
$s;
Psalm output (using commit f5a0622):
INFO: Trace - 34:1 - $s: non-empty-string|null
https://psalm.dev/r/b8afb83865
<?php
class Assert {
/**
* @psalm-pure
* @psalm-template UnexpectedType of object
* @psalm-param class-string<UnexpectedType> $class
* @psalm-assert !UnexpectedType $value
* @psalm-assert !class-string<UnexpectedType> $value
*
* @param object|string $value
* @param string $class
* @param string $message
*
* @throws InvalidArgumentException
*/
public static function isNotA($value, $class, $message = ''): void {}
}
/** @param Error|Exception $p */
function f(Throwable $p): Error {
Assert::isNotA($p, Exception::class);
return $p;
}
Psalm output (using commit f5a0622):
No issues!
@weirdan
That works
when I said "relevant" I meant - that is another case that I need to null | X, so it should be nullOrIsNotA
Ah, I see.
I don't ever see Psalm supporting this, sorry
This seems to already work with more "traditional" assertions: is there an architectural reasoning why union types cannot be expanded to || expressions?
See https://psalm.dev/r/890b8abfff for reference
I found these snippets:
https://psalm.dev/r/890b8abfff
<?php
/** @param mixed $foo */
function restrictType($foo): ?int
{
\assert(is_int($foo) || null === $foo);
return $foo;
}
Psalm output (using commit 953be61):
No issues!
It’s mainly about the complexity of type parsing.
Types and assertions have a different syntax, but some types are valid assertions. Many valid assertions are not, on the other hand, valid types.
In order to not make life too difficult for me or anyone else updating the type parsing logic, I have to add some limits on what’s allowable.
Makes sense, thank you!
On Tue, May 26, 2020, 14:40 Matthew Brown notifications@github.com wrote:
It’s mainly about the complexity of type parsing.
Types and assertions have a different syntax, but some types are valid
assertions. Many valid assertions are not, on the other hand, valid types.In order to not make life too difficult for me or anyone else updating the
type parsing logic, I have to add some limits on what’s allowable.—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/vimeo/psalm/issues/3444#issuecomment-633999088, or
unsubscribe
https://github.com/notifications/unsubscribe-auth/AABFVEEMFZDJQ3GVRRQVMO3RTO2D3ANCNFSM4NIVTV7Q
.
Types and assertions have a different syntax, but some types are valid assertions.
That's what I always wondered. What would be a trivial example of a valid assertion that is an invalid type otherwise?
What would be a trivial example of a valid assertion that is an invalid type otherwise?
!null, falsy are two examples