Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Reuse union class-string sometimes in non-covariant generic #12256

Closed Answered by ondrejmirtes
rudiedirkx asked this question in Support
Discussion options

I have a custom phpstan-type that I import all over the place to indicate a kind of Test:

/**
 * @phpstan-type CompareableTestType Test1|Test2|Test3
 */
interface CompareableContract {
 /** @return CompareableTestType */
 public function getTest();

I don't want to type those 3 (for now) classes everywhere, so they're a custom type.

I also have a generic UserTest class with template TTestType with a $test property of type TTestType:

$usertest = $user->getLastTest('test1'); // UserTest<Test1> (from custom extension for getLastTest())

Both of those work perfectly separately, but now I want to combine them:

/**
 * @phpstan-import-type CompareableTestType from CompareableContract
 */
class TestResult implements CompareableContract {
 public function __construct(
 /** @var UserTest<CompareableTestType> */
 protected UserTest $usertest,
 ) {}

Nooo, that's not what I mean! I want $usertest to be a UserTest<Test1>|UserTest<Test2>|UserTest<Test3>, not a UserTest<Test1|Test2|Test3>.

How do I unwrap/ungeneric that correctly in the constructor argument @var? Is that possible? Or should I define the custom type CompareableTestType differently? Is that possible??

(Non-)covariance is killing me! 😆 Not in code/reality, but in Stan.

You must be logged in to vote

How do I unwrap/ungeneric that correctly in the constructor argument

To achieve UserTest<Test1>|UserTest<Test2>|UserTest<Test3> instead of UserTest<Test1|Test2|Test3>, I think you need a custom PHPDoc type, something like expand-union<...>.

Replies: 2 comments 2 replies

Comment options

I could make CompareableTestType a real interface, but then phpstan won't know that it's always a Test, because anything could implement an interface.

Is there a way to tell phpstan that I promise that MyInterface is always a Test (abstract)? That helps in other situations too, even though it's very dubious and unwise. There are 10 concrete Test classes, 4 of them are MyInterface, and MyInterface is never used anywhere else, so it's always automatically Test&MyInterface.

You must be logged in to vote
2 replies
Comment options

Is there a way to tell phpstan that I promise that MyInterface is always a Test

Yes: https://phpstan.org/writing-php-code/phpdocs-basics#enforcing-class-inheritance-for-interfaces-and-traits

Comment options

That doesn't reference exact classes, only the parent. That's probably good, you don't want a circular reference, but it's not what I meant.

Comment options

How do I unwrap/ungeneric that correctly in the constructor argument

To achieve UserTest<Test1>|UserTest<Test2>|UserTest<Test3> instead of UserTest<Test1|Test2|Test3>, I think you need a custom PHPDoc type, something like expand-union<...>.

You must be logged in to vote
0 replies
Answer selected by rudiedirkx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Category
Support
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /