psalm: Incorrect RedundantCast and lack of MixedArgumentTypeCoercion with array-key type

I have an array which contains both string and integer keys. After upgrading from Psalm 3.14.2 to 4.3.1, I ran into a couple different issues with this code:

  1. When looping over the array in the constructor method, Psalm fails to show a MixedArgumentTypeCoercion error when passing the array key to a function that requires a string. However, Psalm correctly shows this error when the same code is placed in another function called by the constructor.
  2. In the function called by the constructor, even though Psalm correctly shows an error when directly passing the array key to a function expecting an int, if I cast the array key to a string to avoid a runtime TypeError, Psalm incorrectly shows a RedundantCast error.
<?php

declare(strict_types=1);

class Thing
{
    public function __construct(
        public string $name,
        public string $value,
    ) {}
}

new Foo([
    new Thing('foo', 'bar'),
    // Foo::__construct will cause the string '123' to end up as an int array key
    new Thing('123', 'baz'),
]);

class Foo
{
    /**
     * @var array<mixed, string>
     */
    public array $map;

    /**
     * @param Thing[] $things
     */
    public function __construct(array $things)
    {
        $this->map = [];

        foreach ($things as $thing) {
            $this->map[$thing->name] = $thing->value;
        }

        // map may now contain some string keys and some int keys

        foreach ($this->map as $key => $value) {
            // $key may be an int, but Psalm fails to produce the error it does inside useMap()
            echo strlen($key);
        }

        $this->useMap();
    }

    public function useMap(): void
    {
        foreach ($this->map as $key => $value) {
            // this line correctly causes an error
            echo strlen($key); // MixedArgumentTypeCoercion: Argument 1 of strlen expects string, parent type of array-key provided

            // this line incorrectly says the cast is redundant
            echo strlen((string) $key); // RedundantCast: Redundant cast to string
        }
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 15 (3 by maintainers)

Commits related to this issue

Most upvoted comments

I proposed something to handle this issue a few month ago: https://github.com/vimeo/psalm/pull/4237.

However, this is something that will probably have a lot of false positives (almost every string array-key is potentially problematic) for only a few actual bug caught

Everything Psalm is saying here is correct.