array shapes 記法は type alias で再利用できる

PHPDoc で連想配列の内容を定義できる方法があります。

[array shapes 記法](https://qiita.com/tadsan/items/bfa9465166c351da37e5)と呼ばれますが、次のように定義することで、定義に合わない呼び出し方を PHPStan や Psalm で検出することができますし、 PhpStorm での補完が効くようになります。

```php
/**
 * @param array{
 *     id: int,
 *     name: string,
 *     email: string,
 *     phone: string,
 * } $person
 */
public function setPerson(array $person)
{
    // ...
}
```

そこら中に配列を使うんじゃなくてちゃんとクラスを作ろうよ。というご意見もごもっともですが、配列に変換せざるを得ない場合もあるでしょ？（あると思います）
そんなときに array shapes 記法でちゃんと PHPDoc に書いてあげるといろいろと便利ですし、ドキュメントにもなりますから連想配列を使う場合にはどんどん使いたいものです。

解決したい問題
---------------

上記のような array shapes 記法で書かれた定義がいくつもの場所で出現する場合があります。

例えば person の配列を扱うメソッドがあるとどうでしょうか


```php
/**
 * @param array<array{
 *     id: int,
 *     name: string,
 *     email: string,
 *     phone: string,
 * }> $people
 */
public function setPeople(array $people)
{
    // ...
}
```

このように定義を配列で囲って定義します。
さらに、people が所属する会社があるとどうでしょうか。

```php
/**
 * @param array{
 *     id: int,
 *     companyName: string,
 *     emproyees: array<array {
 *         id: int,
 *         name: string,
 *         email: string,
 *         phone: string,
 *     }>
 * } $company
 */
public function setCompany(array $company)
{
    // ...
}
```

同じ定義があちこちに出てきます。
これらの定義を `@return` でも使っているとなると、さすがに書くのが面倒だしメンテナンスも大変です。

解決方法 (type aliases)
------------------------

クラスの宣言部で type alias を定義することで、一旦定義した array shapes を使い回すことができます。

```php
/**
 * @psalm-type PersonArray = array{
 *     id: int,
 *     name: string,
 *     email: string,
 *     phone: string,
 * }
 * @psalm-type CompanyArray = array{
 *     id: int,
 *     companyName: string,
 *     emproyees: PersonArray[],
 * }
 */
class Company
{
    /**
     * PersonArray $person
     */
    public function setPerson(array $person): void
    {
        // ...
    }

    /**
     * PersonArray[] $person
     */
    public function setPeople(array $people): void
    {
        // ...
    }

    /**
     * @param CompanyArray $company
     */
    public function setCompany(array $company): void
    {
        // ...
    }
    
    /**
     * @return CompanyArray
     */
    public function getCompany(): array
    
}
```

重複していた定義を綺麗にできました。
さらに `CompanyArray` を定義しても良いかもしれません。

また、他のクラスで定義された型エイリアスをインポートすることもできます。


```php
/**
 * @psalm-import-type PersonArray from PersonClass
 */
class Company
```

`@psalm-type` や `@psalm-import-type` を使っていますが、PHPStan でも同様に `@phpstan-type` や `phpstan-import-type` があります。僕は PHPStan と Psalm の両方を回しているプロジェクトがありますが、`@psalm-type` や `@psalm-import-type` だけをセットしていても PHPStan も認識してくれているようです。

type alias を使った場合に PhpStorm では次のちょっとした問題があります。

```php
/**
 * @param PersonArray $person
 * @return PersonArray
 */
public function changeName(array $person, string $newName): array
```

ここでの `PersonArray` は type alias です。array の型を補足するだけのために PHPDoc を書いています。
PhpStorm はこの場合に `PHPDoc comment signature is not complete` という warning を与えてくれます。
`$newName` の定義も書きなさいと仰っています。`PersonArray` のところが array shapes を直に書いている場合はこの warning は出ませんので、解析が追いついていないのかもしれません。

参考文献
-------

- https://qiita.com/tadsan/items/bfa9465166c351da37e5
- https://phpstan.org/writing-php-code/phpdoc-types#local-type-aliases
- https://psalm.dev/docs/annotating_code/type_syntax/utility_types/#type-aliases