phpstorm-attributes icon indicating copy to clipboard operation
phpstorm-attributes copied to clipboard

[ArrayShape] for maps/associative arrays as dynamic array indexes

Open Justinas-Jurciukonis opened this issue 1 year ago • 4 comments

Right now if I have following attribute

class Shema {
   #[ArrayShape(['string' => Property::class])]
    public array $properties;
}

$schema->properties['prop1'] = new Property();
$shcema->properties['prop2'] = new Property();

PHPStorm (PhpStorm 2023.3.4) will report on key Value should be one of: 'string' (why value?).

Is there any way to show possible index values? Like "string" / "int" / "SomeEnum->getValues()"

Screenshot 2024-02-28 at 10 59 34

Justinas-Jurciukonis avatar Feb 28 '24 11:02 Justinas-Jurciukonis

I think it's worth using native PHP syntax:

/**
 * @var array<string, Property>
 */
public array $properties;

// or
/**
 * @var array{prop1: Property, prop2: Property}
 */
public array $properties;

// or
/**
 * @var array{
 *     prop1: Property, 
 *     prop2: Property,
 *     ...<string, Property>
 * }
 */

SerafimArts avatar Jun 21 '24 12:06 SerafimArts

array<string, Property> is not an array shape (based on the concept of array shapes defined by tools like phpstan and psalm that introduced it) but a generic array. If support is added, it should probably be a separate attribute with a different signature (taking the key type and the value type, not an array describing the shape)

stof avatar Jan 17 '25 15:01 stof

In this case, the 'string' key is a string type (I could write array<non-empty-string, Property> or array<array-key, Property>), so this generalization is valid for autocomplete property to get an array element by the 'string' key, like: $properties['string'].

And since the generic is an unsealed array shape (because does not describe the structure, but only the types), it also allows the Property to be autocompleted from any other keys, like: $properties['prop1'] or $properties['prop2'].

SerafimArts avatar Jan 17 '25 16:01 SerafimArts

@SerafimArts the whole point of the ArrayShape attribute is to define an array shape, i.e. an array with known keys (and potentially different types per keys). In phpdoc, this is written as array{prop1: PropertyType, prop2: PropertyType} (notice the { } vs < > for a generic array).

A generic array is not the equivalent of an unsealed array shape.

Your example #[ArrayShape(['string' => Property::class])] in the issue description has already a proper meaning, which is the type array{string: Property} (i.e. an array with a string key, not an array with a key being any string)

stof avatar Jan 17 '25 16:01 stof