gamez/typed-collection
gamez/typed-collection adds type-safe, strongly typed collections to Laravel and PHP. Define the item type once, keep the familiar Collection API, and get better IDE autocompletion, static analysis, and early errors when invalid values are added.
Architecture fit. gamez/typed-collection is a thin abstract subclass of Illuminate\Support\Collection that validates every item against a declared type on write and construction. Architecturally it is a boundary-hardening tool, not a framework: it adds no service provider, no config, no facade, no persistence layer. It fits where collections already exist as domain carriers — aggregate child lists, value-object bags, API response payloads, config-derived sets — and where the team already treats Collection as a public API surface. It is a poor fit for anything that needs Eloquent semantics (Model::newCollection()), lazy/streaming datasets, or collections used purely as ad-hoc arrays in controllers.
Integration feasibility. Low. composer require gamez/typed-collection, extend the base class, declare the expected item type, done. The real integration work is not installation but governance: deciding which collections become typed, and enforcing that convention in review and static analysis. Because it subclasses a vendor class, the Illuminate version constraint is the binding compatibility factor and must be checked against the application's current Laravel/PHP floor before anything else.
Technical risk. Concentrated in three areas:
map, filter, merge, flatten and similar methods return new static, so a typed subclass can be produced by a callback whose output violates the declared type. Confirm whether this package re-validates on those paths or only on push/put/offsetSet; if it does not, typing is advisory at transform boundaries.Key questions.
InvalidArgumentException vs. TypeError)?null items?@extends Collection<int, Foo>)? Without these, IDE and CI feedback is weak and the safety gain is runtime-only.illuminate/collections and PHP version constraint matrix, and is it tested against the Laravel majors we run?new? Relevant only for large hydration paths.json_encode/queue round-trip lossless, and how is rehydration into the typed class expected to happen?Stack fit. Native fit for Laravel 9–12 / PHP 8.1+ codebases that already depend on illuminate/collections; it also works framework-agnostically in any Composer project pulling Illuminate components. It does not slot in as an Eloquent collection replacement — that route requires newCollection() overrides and is a different, larger change.
Migration path. Do not retrofit. Introduce it forward-only in new or actively-refactored domain code, one bounded context at a time, and convert existing Collection returns only where a type violation would be a real defect (money, IDs, DTOs). For legacy arrays flowing in, wrap at the adapter layer and let the constructor do the validation. If the package fails the invariant-leakage question above, the fallback is a ~50-line in-house TypedCollection — cheap enough that build-vs-buy should be decided on the static-analysis story, not on code volume.
Compatibility. Pin the illuminate/collections constraint explicitly rather than relying on transitive resolution, and verify it against the framework version you actually deploy. Watch for collisions with packages that return their own Collection subclasses — those objects will fail type checks unless they extend your typed base. Confirm Laravel's package-discovery is a non-issue (it is: no provider).
Sequencing.
map/filter/merge/push/put behaviour. This is the go/no-go gate.composer why-not style check) so future contributors do not silently revert to bare Collection.Maintenance. Near-zero ongoing cost: no migrations, no background jobs, no config, no runtime services. The ongoing cost is convention maintenance — keeping new code aligned and keeping the package constraint current with Laravel upgrades. Each Laravel major is a small manual compatibility review because the maintainer must ship a release first; budget a ticket per framework upgrade. Vendor the package or keep a fork branch ready given the single-maintainer profile.
Support. Community-only, no SLA, no commercial backing. Acceptable for a non-critical library with a trivial fallback; not acceptable if your release process requires vendor support agreements. The mitigation is that the entire dependency is auditable and replaceable.
Scaling. Runtime overhead is one type check per item on write and on bulk construction — negligible against typical request volumes, but O(n) when hydrating large arrays, so avoid it in hot loops over tens of thousands of rows. Memory profile is identical to Collection. No new failure surface at the infrastructure layer.
Failure modes.
Collection and the guarantee quietly disappears at a method boundary. Mitigate with return type declarations (: MyTypedCollection) enforced by static analysis.Ramp-up. Under an hour for a Laravel-fluent engineer: one class, one property, one exception. The larger ramp is cultural — teaching reviewers to spot where a typed collection is bypassed. A short ADR plus two example classes in the codebase is sufficient onboarding material.
TPM read. With 46 stars, zero dependents, and an opportunity score near 32, this is a low-cost, low-certainty bet: cheap to trial, cheap to abandon, but not a validated ecosystem choice. Recommend a two-day spike gated on the transform-invariant question and PHPStan annotations; approve only for new domain code, and treat the in-house alternative as a live option rather than a fallback.
How can I help you explore Laravel packages today?