psalm icon indicating copy to clipboard operation
psalm copied to clipboard

A trait with type parameters can not pass the type parameter to another trait

Open vzz3 opened this issue 1 year ago • 3 comments

Example: https://psalm.dev/r/37284614ae

The class RepA uses the trait TraitBase with the type parameter A, which works as expected. However, if the TraitBase is used by the trait TraitSub and the TraitSub is used by a class, which finally defines the type parameter (RepB or RepC), the following error is produced:

ERROR: [ArgumentTypeCoercion](https://psalm.dev/193) - 44:30 - Argument 1 of RepB::configureBase expects class-string<B>, but parent type class-string<T:TraitSub as object> provided

vzz3 avatar Dec 29 '23 03:12 vzz3

I found these snippets:

https://psalm.dev/r/37284614ae
<?php

/**
 * @template T
 */
trait TraitBase {
	
	/**
	 * @var class-string<T>
	 */
	protected string $_ObjectType;
    
    /**
	 * 
	 * @param class-string<T>	$yObjectType
	 * @return void
	 */
	protected function configureBase(string $yObjectType): void {
		$this->_ObjectType = $yObjectType;
	}
}

/**
 * @template T
 */
trait TraitSub {
    /**
	 * @use TraitBase<T>
	 */
	use TraitBase;

    /**
	 * @var string
	 */
    protected string $_OtherString;
    
    /**
	 * 
	 * @param class-string<T>	$yObjectType
     * @param string	$yOtherString
	 * @return void
	 */
	protected function configureSub(string $yObjectType, string $yOtherString): void {
        $this->configureBase($yObjectType);
		$this->_OtherString = $yOtherString;
	}
}

class A {}
class B {}
class C {}

class RepA {
    /**
	 * @use TraitBase<A>
	 */
	use TraitBase;
    
    public function __construct() {
    	$this->configureBase(A::class);
    }
}

class RepB {
    /**
	 * @use TraitSub<B>
	 */
	use TraitSub;
    
    public function __construct() {
    	$this->configureSub(B::class, 'repB test');
    }
}

class RepC {
    /**
	 * @use TraitSub<C>
	 */
	use TraitSub;
    
    public function __construct() {
    	$this->configureSub(C::class, 'repB test');
    }
}
Psalm output (using commit a75d26a):

ERROR: ArgumentTypeCoercion - 44:30 - Argument 1 of RepB::configureBase expects class-string<B>, but parent type class-string<T:TraitSub as object> provided

ERROR: PropertyNotSetInConstructor - 64:7 - Property RepB::$_ObjectType is not defined in constructor of RepB or in any methods called in the constructor

ERROR: ArgumentTypeCoercion - 44:30 - Argument 1 of RepC::configureBase expects class-string<C>, but parent type class-string<T:TraitSub as object> provided

ERROR: PropertyNotSetInConstructor - 75:7 - Property RepC::$_ObjectType is not defined in constructor of RepC or in any methods called in the constructor

psalm-github-bot[bot] avatar Dec 29 '23 03:12 psalm-github-bot[bot]

Related (I think)

@psalm-import-type CustomType from \Foo\BarTrait

doesn't appear to work either.

complains that it can't find a class or Enum named \Foo\BarTrait

bkdotcom avatar Jan 28 '24 21:01 bkdotcom

Related (I think)

@bkdotcom Not really. Type aliases and generics are two very different things. For your issue see #10625

weirdan avatar Jan 31 '24 22:01 weirdan