Fixing PHPStan map() Type Errors with Laravel Eloquent Collections

TL;DR
Refactor property declaration for collection as
/** * @property Collection<int, Comment> $comment ...
Overview
When using PHPStan (or Larastan) with Laravel, you may encounter confusing type errors when calling collection methods like map() on Eloquent relationships. A very common one looks like this:
Parameter #1 $callback of method
Collection::map()expects callable(Model, int), Closure(Comment) given
This blog explains why this happens, what PHPStan is actually telling us, and the correct, long-term fix that works cleanly across your codebase.
The Problem Scenario
Consider a Laravel model relationship:
$user->comments->map(
fn (Comment $comment) => [
'comment' => $comment->text,
'created_date' => $answer->created_at?->toArray(),
]);
At runtime, this works perfectly. However, PHPStan reports an error similar to:
Parameter #1 $callback of method Illuminate\Support\Collection::map() expects callable(Illuminate\Database\Eloquent\Model, int) Closure(Comment) given
Why This Happens
1. Eloquent Collections Are Generic
Laravel Collections are generic:
Collection<TKey, TValue>
If PHPStan does not know the exact model type inside the collection, it falls back to:
Collection<int, Model>
So PHPStan assumes your callback must accept any Model, not a specific one like Comment.
2. Property Annotations Are Often Incomplete
Many projects annotate Eloquent properties like this:
@property Illuminate\Database\Eloquent\Collection<Comment> $comments
Unfortunately, this is incomplete for PHPStan.
PHPStan requires both key and value types.
❌ What PHPStan Sees (Incorrect)
Collection<int, Model>
✅ What We Want PHPStan to See
Collection<int, Comment>
The Correct Fix
✅ Option 1: Fix the Property Annotation
Update the PHPDoc on your model by using PHPStan generics:
/**
* @property \Illuminate\Database\Eloquent\Collection<int, Comment> $answers
*/
class Comment extends Model { // ... }
This tells PHPStan exactly what the collection contains.
⭐ Option 2 (Recommended): Type the Relationship Method
If answers is an Eloquent relationship, this is the best and cleanest solution.
use Illuminate\Database\Eloquent\Relations\HasMany;
/** * @return HasMany<Comment, $this> */
public function comments(): HasMany {
return $this->hasMany(Comment::class);
}
Benefits:
No need for
@propertyannotationsPHPStan auto-infers the collection type
Works everywhere the relationship is used
Result: Clean map() Usage
After applying either fix, this works without errors:
$user->comments->map(
fn (Comment $comment) => [
...
]);
✅ PHPStan passes
✅ Strong typing preserved
Common Anti-Patterns to Avoid
❌ Removing the type hint:
fn ($comment) => [...]
❌ Widening the type unnecessarily:
fn (Model $comment) => [...]
These silence PHPStan but lose static safety.
Takeaways
PHPStan errors here are not false positives
The issue is almost always missing or incomplete generics
Always prefer typing relationship return values
This fix also applies to
filter,each,flatMap,reduce, etc.
Final Recommendation for Teams
Always annotate Eloquent relationship methods with generics.
This single practice eliminates an entire class of PHPStan errors and makes your Laravel codebase safer and more maintainable.
For more details on generics in PHP, please read here
Happy static analyzing! 🚀



