Skip to content

Method with this-out cannot apply union to template type of array-key #13374

Closed

Description

@axlon

Bug report

It appears that PHPStan does not add "normal" types to $this when using @phpstan-this-out.

It appears that PHPStan is unable to union a template type via this-out (and presumably static-out) when the existing template type has a boundary of array-key

Assume a class with the following template types: K of array-key and V

@phpstan-this-out static<int, V> Works as expected
@phpstan-this-out static<int|K, V> Does not work, int is ignored
@phpstan-this-out static<K, V> Works as expected
@phpstan-this-out static<K|T, V> Does not work, T is ignored

Code snippet that reproduces the problem

https://phpstan.org/r/ed653e7f-15e0-4156-b684-59fa0c0cf31a

Expected output

No errors, the method should add int to the TKey template type of $this but instead it is ignored.

Did PHPStan help you today? Did it make you happy in any way?

No response

Activity

  1. axlon commented on Aug 9, 2025

    @axlon
    ContributorAuthor

    It appears that it has nothing to do with part of the union not being a template type; I am able to reproduce the issue with a union of two template types: https://phpstan.org/r/be6cc85e-bd4a-482a-803d-61cb0fd95b12

    My best guess is that the of array-key on the class template type is causing this, because when I remove (line 7) it it does seem to work: https://phpstan.org/r/425539f8-daa0-467c-9330-ac6661a124d0

  2. changed the title [-]Method with this-out does not support union of template type and normal type[/-] [+]Method with this-out cannot apply union to template type with boundary[/+] on Aug 9, 2025
  3. changed the title [-]Method with this-out cannot apply union to template type with boundary[/-] [+]Method with this-out cannot apply union to template type of array-key[/+] on Aug 9, 2025
  4. VincentLanglet commented on Aug 10, 2025

    @VincentLanglet
    Contributor

    array-key is a benevolent union of int and string.
    so int|(int|string) is simplified to (int|string) in your phpdoc which is your template,
    so int|TKey is indeed simplified to TKey.

    You won't get the issue by using int|string instead.
    https://phpstan.org/r/3577d04a-545c-4431-b404-9ee5d00c86d8

  5. ondrejmirtes commented on Sep 2, 2025

    @ondrejmirtes
    Member

    int|TKey shouldn't be normalized. When doing type normalization (https://phpstan.org/developing-extensions/type-system#type-normalization) parts of a union type are consumed by another when $eaterType->isSuperTypeOf($consumedType)->yes().

    But with int and TKey neither type is a super type of each other.

  6. VincentLanglet commented on Sep 3, 2025

    @VincentLanglet
    Contributor

    Let's say $int is int et $tkey is TKey.

    The call

    $int->isSuperTypeOf($tkey)
    

    is triggering

    $tkey->isSubTypeOf($int)
    

    since TKey is a CompoundType because it's an UnionType(benevolent one)

    The implementation is

    public function isSubTypeOf(Type $type): IsSuperTypeOfResult
    	{
    		/** @var TBound $bound */
    		$bound = $this->getBound();
    		if (
    			!$type instanceof $bound
    			&& !$this instanceof $type
    			&& !$type instanceof TemplateType
    			&& ($type instanceof UnionType || $type instanceof IntersectionType)
    		) {
    			return $type->isSuperTypeOf($this);
    		}
    
    		if (!$type instanceof TemplateType) {
    			return $type->isSuperTypeOf($this->getBound());
    		}
    
    		if ($this->getScope()->equals($type->getScope()) && $this->getName() === $type->getName()) {
    			return $type->getBound()->isSuperTypeOf($this->getBound());
    		}
    
    		return $type->getBound()->isSuperTypeOf($this->getBound())
    			->and(IsSuperTypeOfResult::createMaybe());
    	}
    

    so we're doing

    if (!$type instanceof TemplateType) {
         return $type->isSuperTypeOf($this->getBound());
    }
    

    which is $int->isSuperTypeOf($benevolentUnionIntOrString) which is YES.

    That's the issue with using a Benevolent bound...

  7. added this to the Generics milestone on Sep 12, 2025
  8. phpstan-bot commented on Sep 9, 2026

    @phpstan-bot
    Contributor

    Closed via merging phpstan/phpstan-src#6376

  9. github-actions commented on Oct 11, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

  10. locked as resolved and limited conversation to collaborators on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions