PHPStan cast to more specific array shape

708 views Asked by At

In my code, I call a method whose return value is typed as follows:

/**
 * @return array<int, array<string,mixed>>
 */
public function fetchAllAssociative(): array;

The subtype array<string, mixed> describes the database record in a generic way. I know exactly what this database record looks like so I want to be more specific in documenting the method:

/**
 * @return array<int, array{id: int, firstName: string, lastName: string}>
 */
public function getAllUsers(): array
{
  return $this->userRepository->fetchAllAssociative();
}

However, PHPStan complains that I should type my methods exactly as fetchAllAssociative:

Method UserRepository::getAllUsers() should return array<int, array{id: int, firstName: string, lastName: string}> but returns array<int, array<string, mixed>>.

Is there a way to cast it? My workaround is to introduce a new variable and use the @var tag but I don't really like it (other static code analysis tools don't like it as well)

/**
 * @return array<int, array{id: int, firstName: string, lastName: string}>
 */
public function getAllUsers(): array
{
  /**
   * @var array<int, array{id: int, firstName: string, lastName: string}> $data
   */
  $data = $this->userRepository->fetchAllAssociative();

  return $data;
}
1

There are 1 answers

0
Watercycle On

I ran into something similar. Unfortunately, I think adding // @phpstan-ignore-next-line to the implementation is the only other thing you can do.

In Typescript, this is basically equivalent to asserting that any is User. Typically this would happen in a "makeUser" function the moment you get and validate the data (e.g. from a database or an http request) so that you can minimize your data as unknown as User usages. Otherwise, this is the consequence.

So, general advise to anyone else running into this, start with your repositories and really make it a point to avoid array<mixed> and mixed, cause it's going to quickly trickle throughout your system.