Search code examples
phpphpstanintersection-types

Specifying keys in array shape in PHPstan


I want to annotate function, tat takes array with at least key "a" and returns the same array shape with newly added key "x". I tried using type intersection like this:

/**
 * @template T of array{a: string}
 * @param T $p
 * @return T&array{x: int}
 */
function addXToArray(array $p) {
    $p['x'] = strlen($p['a']);
    return $p;
}

$result = addXToArray(['a' => 'hello']);

This is obviously not the correct way, because PHPstan complains (on level 10 with "Treat PHPDoc types as certain"):

PHPDoc tag @return contains unresolvable type.

I use template T because I need to preserve any other keys that may be present in the argument.

How do I correctly annotate the function?


Solution

  • I use template T because I need to preserve any other keys that may be present in the argument.

    That's perhaps the hint needed here.

    More specifically, you are using T as an intersection type for @return with something that is not a type: array{} shape.

    As has been outlined in phpstan / phpstan Array intersection support #4703 (github.com) the ampersand & of an intersection type does not compute array shapes and likewise it does not the T template in your example.

    What you may expect from PHPStan according to ondrejmirtes in February 2024 is as following:

    PHPStan WILL NOT add support for "array intersections" but WILL ADD support for "array shapes with extra keys with known type" using this syntax: array{a: mixed, ...<array-key, mixed>} (same as recently added in Psalm).

    Please follow this issue to get the updates: #8438 (github.com)