vimeo / psalm

A static analysis tool for finding errors in PHP applications
https://psalm.dev
MIT License
5.55k stars 659 forks source link

[Question] Any ability to manually specify templated type subtraction? #5661

Open someniatko opened 3 years ago

someniatko commented 3 years ago

I try to type the following function of phpoption/phpoption library: https://psalm.dev/r/1d6b9238da

The problem is, in Option::fromValue() I want to specify that V template param is not N. Is it possible to do this? If not, how should I type such a function?

Thank you!

psalm-github-bot[bot] commented 3 years ago

I found these snippets:

https://psalm.dev/r/1d6b9238da ```php */ static function fromValue($value, $noneValue = null) { return $value === $noneValue ? new None() : new Some($value); } /** @return T */ abstract function getOrThrow(); } /** * @template T * @extends Option */ final class Some extends Option { /** @param T $value */ function __construct(private $value) {} function getOrThrow() { return $this->value; } } /** @extends Option */ final class None extends Option { function getOrThrow() { throw new \RuntimeException; } } ``` ``` Psalm output (using commit ecd5e3b): ERROR: InvalidReturnStatement - 14:16 - The inferred type 'None|Some<(N:fn-option::fromvalue as null|scalar)|(V:fn-option::fromvalue as mixed)>' does not match the declared return type 'Option' for Option::fromValue ERROR: InvalidReturnType - 11:16 - The declared return type 'Option' for Option::fromValue is incorrect, got 'None|Some<(N:fn-option::fromvalue as null|scalar)|(V:fn-option::fromvalue as mixed)>' ```
someniatko commented 3 years ago

The reason I want to type this, is to make the following work:


/** @var string|null */
$stringOrNull = 'str';

$opt = Option::fromValue($stringOrNull, null);
/** @psalm-trace $opt */

for now @psalm-trace returns Option<string|null>, and I'd like to have Option<string>.

someniatko commented 3 years ago

BTW, I found it a bit strange that in the following case no errors are emitted: https://psalm.dev/r/69f073e7a4

psalm-github-bot[bot] commented 3 years ago

I found these snippets:

https://psalm.dev/r/69f073e7a4 ```php */ static function fromValue($value, $noneValue = null) { return $value === $noneValue ? new None() : new Some($value); } /** @return T */ abstract function getOrThrow(); } /** * @template T * @extends Option */ final class Some extends Option { /** @param T $value */ function __construct(private $value) {} function getOrThrow() { return $this->value; } } /** @extends Option */ final class None extends Option { function getOrThrow() { throw new \RuntimeException; } } ``` ``` Psalm output (using commit 419114e): No issues! ```
someniatko commented 1 year ago

BTW, I found it a bit strange that in the following case no errors are emitted: https://psalm.dev/r/69f073e7a4

Now there are errors, great!

psalm-github-bot[bot] commented 1 year ago

I found these snippets:

https://psalm.dev/r/69f073e7a4 ```php */ static function fromValue($value, $noneValue = null) { return $value === $noneValue ? new None() : new Some($value); } /** @return T */ abstract function getOrThrow(); } /** * @template T * @extends Option */ final class Some extends Option { /** @param T $value */ function __construct(private $value) {} function getOrThrow() { return $this->value; } } /** @extends Option */ final class None extends Option { function getOrThrow() { throw new \RuntimeException; } } ``` ``` Psalm output (using commit 722bec7): ERROR: DocblockTypeContradiction - 15:16 - Cannot resolve types for $noneValue - docblock-defined type N:fn-option::fromvalue as null|scalar does not contain V:fn-option::fromvalue as (N:fn-option::fromvalue as null|scalar)|(S:fn-option::fromvalue as mixed) ERROR: DocblockTypeContradiction - 15:16 - Cannot resolve types for $value - docblock-defined type V:fn-option::fromvalue as (N:fn-option::fromvalue as null|scalar)|(S:fn-option::fromvalue as mixed) does not contain N:fn-option::fromvalue as null|scalar ```