Search code examples
genericsphp-7.4psalm-php

Extend a generic of an interface using psalm


I'm getting used to use psalm, but I'm facing an issue. I already have this structure in C# and it worked out for me. I didn't really get how I could solve this using psalm.

I have a ContextInterface and another implementing it.

interface ContextInterface { public function getProductId(): int; }
interface SingleContextInterface extends ContextInterface { public function getWidth(): int; }

Additionally I have strategy interfaces for them.

/**
 * @template-covariant T as ContextInterface
 * @psalm-immutable
 */
interface CalculatorInterface
{
    /**
     * @psalm-param T $context
     */
    public function getPrice(ContextInterface $context): int;
}

/**
 * @template T as SingleContextInterface
 * @template-extends CalculatorInterface<T>
 * @psalm-immutable
 */
interface SingleDimensionalPriceCalculator extends CalculatorInterface { }

And one class implementing the interface:

/**
 * @template T as SingleContextInterface
 * @template-implements SingleDimensionalPriceCalculator<T>
 * @psalm-immutable
 */
class SingleDimensionCalculator implements SingleDimensionalPriceCalculator
{
    /**
     * @psalm-param SingleContextInterface $context
     */ 
    public function getPrice(ContextInterface $context): int
    {
        $context->getWidth();
        return 1;
    }
    
}

For the getWidth() method call I receive the following error:

ERROR: ImpureMethodCall - 37:19 - Cannot call an possibly-mutating method SingleContextInterface::getWidth from a mutation-free context

The example on psalm.dev

Of course the real case is more complicated and contains more interfaces.


Solution

  • I figured, the problem was that I marked the template on CalculatorInterface as covariant. I can just extend it without, it being covariant.

    Example on psalm.dev