Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Typed Laravel Package

spatie/typed

Userland improved PHP type system with type inference and runtime checking: generics, union types, typed collections/lists, tuples, and structs. Proof-of-concept package from Spatie to add stronger type guarantees without language-level support.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require spatie/typed
    

    Add to composer.json under require-dev if using for testing/analysis only.

  2. First Use Case Define a typed list in a test file or analysis script:

    use Spatie\Typed\Array\TypedList;
    
    $users = new TypedList(['John', 'Jane'], 'string');
    $users->push('Doe'); // Valid
    $users->push(123);   // Throws \Spatie\Typed\Exceptions\TypeMismatchException
    
  3. Where to Look First

    • README for core features (TypedList, TypedTuple, TypedStruct).
    • API Docs for method signatures.
    • Examples for practical use cases.

Implementation Patterns

Typed Collections

Workflow for TypedLists

// Define a list of users with strict typing
$activeUsers = new TypedList([], User::class);

// Safe operations
$activeUsers->push($user); // Valid if $user is User
$activeUsers->filter(fn(User $u) => $u->isActive()); // Type-safe filtering

Integration with Laravel

  • Use in Form Requests for validation:
    public function rules(): array
    {
        $validated = new TypedList($this->input('tags'), 'string');
        return ['tags' => ['array', 'max:5']];
    }
    
  • Service Layer for typed responses:
    public function getUsers(): TypedList
    {
        return new TypedList(User::all(), User::class);
    }
    

Structs for DTOs

Pattern for Data Transfer Objects

use Spatie\Typed\Struct;

class UserDTO extends Struct
{
    public string $name;
    public int $age;
    public bool $isAdmin;
}

// Usage
$userDto = new UserDTO(['name' => 'Alice', 'age' => 30, 'isAdmin' => false]);
$userDto->name = 'Bob'; // Valid
$userDto->invalidProp = 'test'; // Throws \Spatie\Typed\Exceptions\InvalidPropertyException

Laravel Integration

  • Replace manual array validation in API Resources:
    public function toArray($request): array
    {
        return (new TypedList($this->resource->tags, 'string'))->toArray();
    }
    
  • Command Handling for typed inputs:
    protected $signature = 'user:create {--name= : Name of the user}';
    protected function handle(): void
    {
        $user = new UserDTO(['name' => $this->option('name'), 'age' => 25]);
        // ...
    }
    

Tuples for Fixed-Length Data

Use Case: Database Result Sets

use Spatie\Typed\Tuple;

$row = new Tuple([1, 'John', true], [int::class, string::class, bool::class]);
$row->get(0); // Returns int (1)
$row->get(1); // Returns string ('John')

Laravel Integration

  • Query Builder for typed results:
    $users = DB::table('users')->get()->map(fn($u) => new Tuple([$u->id, $u->name], [int::class, string::class]));
    

Gotchas and Tips

Pitfalls

  1. Runtime Overhead

    • Typed collections add runtime checks. Avoid in performance-critical paths (e.g., bulk inserts).
    • Mitigation: Use plain arrays in hot loops; wrap results afterward.
  2. Strict Typing in Structs

    • Undefined properties throw exceptions. Use allowDynamicProperties() if needed:
      class FlexibleStruct extends Struct {
          public function __construct(array $data) {
              $this->allowDynamicProperties();
              parent::__construct($data);
          }
      }
      
  3. Laravel Eloquent Conflicts

    • Avoid naming struct properties the same as Eloquent methods (e.g., create). Use underscores:
      class UserStruct extends Struct {
          public string $name;
          public int $age;
          // No 'created_at' property
      }
      
  4. Type Hints in IDEs

    • Some IDEs (e.g., PHPStorm) may not auto-detect TypedList generics. Manually specify types:
      /** @var TypedList<User> */
      $users = new TypedList(User::all(), User::class);
      

Debugging Tips

  • Enable Strict Mode for early catches:
    TypedList::setStrictMode(true); // Throws on first type mismatch
    
  • Custom Exceptions: Extend \Spatie\Typed\Exceptions\TypeMismatchException for domain-specific errors.

Extension Points

  1. Custom Validators Override validate() in TypedList:

    $customList = new TypedList([], 'string');
    $customList->validate = fn($value) => strlen($value) > 3;
    
  2. Type Aliases Define reusable types:

    $stringList = new TypedList([], 'string');
    $emailList = new TypedList([], fn($value) => filter_var($value, FILTER_VALIDATE_EMAIL));
    
  3. Laravel Service Provider Bind typed collections to the container:

    $this->app->bind(TypedList::class, fn() => new TypedList([], User::class));
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport