如何使用 PhpStan 输入自定义映射构造函数,以便在 BackedEnum 值上索引的实例将保留确切的键类型?

问题描述 投票:0回答:1

抱歉标题令人困惑,我不确定如何最好地总结问题。

我有一个采用

array-key
类型键的自定义地图类,并且我正在尝试创建一个具有更严格
value-of<BackedEnum>
类型键的实例。 PhpStan 使用枚举值作为键正确检测数组,但在创建自定义地图的实例时,它仅检测字符串。 有没有办法输入地图构造函数以便保留
value-of<Code>
键类型?

游乐场示例:
https://phpstan.org/r/6d38999c-fbfc-4b26-a811-9822c3571e23

<?php declare(strict_types=1);

/**
 * @template K of array-key
 * @template V
 */
class Map
{
    /**
     * @var array<K, V>
     */
    private array $array;

    /**
     * @var array<K>
     */
    private array $keys;

    /**
     * @param array<K, V> $a
     */
    final public function __construct(array $a = [])
    {
        $this->array = $a;
        $this->keys = \array_keys($a);
    }

    /**
     * @return array<K, V>
     */
    public function toArray(): array
    {
        return $this->array;
    }

    /**
     * @return array<K>
     */
    public function getKeys(): array
    {
        return $this->keys;
    }

    // ...
}

enum Code: string
{
    case FOO = 'foo';
    case BAR = 'bar';
}


class Test
{

    /**
     * This works.
     *
     * @return array<value-of<Code>, string>
     */
    public static function testArray(): array
    {
        return [
            Code::FOO->value => 'foz',
            Code::BAR->value => 'baz',
        ];
    }

    /**
     * This fails as expectd.
     *
     * @return array<value-of<Code>, string>
     */
    public static function testFailingArray(): array
    {
        return [
            Code::FOO->value => 'foz',
            Code::BAR->value => 'baz',
            'wrong' => 'nogood',
        ];
    }

    /**
     * FIXME This fails because the type infered from the constructor call is `Map<string, string>`.
     *
     * @return Map<value-of<Code>, string>
     */
    public static function testMap(): Map
    {
        return new Map([
            Code::FOO->value => 'foz',
            Code::BAR->value => 'baz',
        ]);
    }
}
php generics phpstan
1个回答
0
投票
  1. 直接注释返回值类型。

    /** @var Map<value-of<Code>, string> */
    return new MapOfCode([
        Code::FOO->value => 'foz',
        Code::BAR->value => 'baz',
    ]);
    
  2. 声明更严格的类型,这也保证了输入数组类型的正确性。

    /** @extends Map<value-of<Code>, string> */
    class MapOfCode extends Map {}
    
    return new MapOfCode([
        Code::FOO->value => 'foz',
        Code::BAR->value => 'baz',
    ]);
    
© www.soinside.com 2019 - 2024. All rights reserved.