-
Notifications
You must be signed in to change notification settings - Fork 653
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
Conditional return on interface template doesn't work #4511
Comments
I found these snippets: https://psalm.dev/r/3cc2367e01<?php
/**
* @template T
*/
interface A
{
/**
* @template V
* @psalm-param V $value
* @psalm-return (T is Closure ? A<V> : never-return)
*/
public function test($value): A;
}
/**
* @template T
* @template-implements A<T>
*/
class B implements A
{
/** @var T */
private $value;
/** @param T $value */
public function __construct($value)
{
$this->value = $value;
}
/**
* @template V
* @psalm-param V $value
* @psalm-return (T is Closure ? B<V> : never-return)
*/
public function test($value): A
{
return new B($value);
}
}
$bs = new B('foo');
$bf = new B(fn (string $a): int => strlen($a));
// this should return B<string>
/** @psalm-trace $result1 */
$result1 = $bf->test('foo');
// this should never-return
/** @psalm-trace $result2 */
$result2 = $bs->test('foo');
https://psalm.dev/r/4484822d18<?php
/**
* @template T
*/
interface A
{
/**
* @template V
* @psalm-param V $value
* @psalm-return (T is Closure ? A<V> : never-return)
*/
public function test($value): A;
}
/**
* @template T
* @template-implements A<T>
*/
class B implements A
{
/** @var T */
private $value;
/** @param T $value */
public function __construct($value)
{
$this->value = $value;
}
public function test($value): A
{
return new B($value);
}
}
$bs = new B('foo');
$bf = new B(fn (string $a): int => strlen($a));
// this should return B<string>
/** @psalm-trace $result1 */
$result1 = $bf->test('foo');
// this should never-return
/** @psalm-trace $result2 */
$result2 = $bs->test('foo');
|
Probably related to #4524 |
Do you have an example of it breaking without |
When both a docblock return type and a signature return type are given, Psalm performs a trivial check to see whether the docblock is more specific than the signature. If that check passes, the docblock is inheritable. Here that check isn't passing, so no inheritance of the docblock occurs (the implementing function specifies its own return type Removing |
I found these snippets: https://psalm.dev/r/68e87c0681<?php
/**
* @template T
*/
interface A
{
/**
* @template V
* @psalm-param V $value
* @psalm-return (T is Closure ? A<V> : never-return)
*/
public function test($value);
}
/**
* @template T
* @template-implements A<T>
*/
class B implements A
{
/** @var T */
private $value;
/** @param T $value */
public function __construct($value)
{
$this->value = $value;
}
public function test($value)
{
return new B($value);
}
}
$bs = new B('foo');
$bf = new B(fn (string $a): int => strlen($a));
// this should never-return
/** @psalm-trace $result2 */
$result2 = $bs->test('foo');
|
|
Here's the trivial check if you want to add support for conditional return types, but it wouldn't be a simple change:
|
Using a conditional return, behaviour is different when defined in interface or in a class.
With class docblock:
https://psalm.dev/r/3cc2367e01
Using interface docblock:
https://psalm.dev/r/4484822d18
I would expect that conditional return works when defined in interfaces too.
Another thing I noticed is that
(T is callable ? something : else)
doesn't work.The text was updated successfully, but these errors were encountered: