Skip to content

[13.x] Improve JsonApiResource collection and array return types - #62356

Closed
tomasvanrijsse wants to merge 2 commits into
laravel:13.xfrom
tomasvanrijsse:json-api-collection-type
Closed

tomasvanrijsse wants to merge 2 commits into
laravel:13.xfrom
tomasvanrijsse:json-api-collection-type

Conversation

@tomasvanrijsse

@tomasvanrijsse tomasvanrijsse commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

JsonApiResource overrides newCollection() to return Illuminate\Http\Resources\JsonApi\AnonymousResourceCollection. The public collection() method is inherited from JsonResource, and its PHPDoc still says it returns Illuminate\Http\Resources\Json\AnonymousResourceCollection.

Static analysis (PHPStan, Larastan) therefore types every SomeJsonApiResource::collection(...) as the plain JSON collection. A controller that declares the JSON:API collection as its return type, which is what it actually returns, gets an error:

public function index(): \Illuminate\Http\Resources\JsonApi\AnonymousResourceCollection
{
    return PostResource::collection(Post::all());
    // Method index() should return JsonApi\AnonymousResourceCollection but returns Json\AnonymousResourceCollection.
}

This PR adds a class-level @method static tag to JsonApiResource, so collection() is typed as the collection it actually creates.

Why a @method tag

#61296 tried to fix this with a PHPDoc-only collection() override that called parent::collection(). This PR adds no method at all:

  • No runtime change. No code is added, so the behaviour of collection(), including preserveKeys, stays exactly the same.
  • Subclasses are untouched. Application resources that already override collection() (with or without a native return type) keep working, because the parent's signature doesn't change.
  • Documentation generators keep inferring the item type. Scramble, for one, documents a collection's items by analysing the collection() call. In an application, overriding collection() in a resource base class changed the generated OpenAPI spec: collection items lost their schema, and the pagination meta and the sparse fieldset parameters disappeared. With a @method tag instead, the spec stayed byte-identical while PHPStan picked up the narrower type.

String-keyed toAttributes(), toRelationships(), toLinks() and toMeta()

These methods are documented as returning a bare array. A resource that overrides toAttributes(Request $request): array without its own PHPDoc inherits that bare array, and PHPStan (level 6 and up) then reports missingType.iterableValue on it. Every resource in an application had to repeat @return array<string, mixed>. The four methods now document array<string, mixed>, so subclasses inherit the value type.

Tests

types/Http/Resources/JsonApi/JsonApiResource.php asserts the return type of collection() for JsonApiResource itself and for a subclass. It also asserts that JsonResource::collection() still returns the plain JSON collection. Without the tag, the two JSON:API assertions fail. It also asserts that a subclass overriding toAttributes() without PHPDoc gets array<string, mixed>; without the PHPDoc change that assertion fails and the subclass reports missingType.iterableValue.

vendor/bin/phpstan analyse --configuration=phpstan.types.neon.dist types/Http/Resources/JsonApi/JsonApiResource.php

Claude

@tomasvanrijsse tomasvanrijsse changed the title [13.x] Type JsonApiResource::collection() as the JSON:API collection [13.x] Improve JsonApiResource collection and array return types Nov 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants