isaiasg3 / laravel-inertia-type-sync
Automatic End-to-End Type Safety between Laravel (FormRequests/JsonResources) and TypeScript for Inertia.js + Vue 3 apps.
Package info
github.com/IsaiasG3/laravel-inertia-type-sync
pkg:composer/isaiasg3/laravel-inertia-type-sync
Requires
- php: ^8.2
- illuminate/console: ^10.0|^11.0|^12.0|^13.0
- illuminate/http: ^10.0|^11.0|^12.0|^13.0
- illuminate/support: ^10.0|^11.0|^12.0|^13.0
- nikic/php-parser: ^5.0
- symfony/finder: ^6.0|^7.0|^8.0
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0
- pestphp/pest: ^2.0|^3.0
- pestphp/pest-plugin-laravel: ^2.0|^3.0
- phpstan/phpstan: ^2.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Automatic End-to-End Type Safety between Laravel and Inertia.js + Vue 3, without any additional PHP annotations.
The Problem
In a typical Inertia.js app, the contract between the backend and frontend lives in two completely separate places that are never validated against each other:
- The
FormRequest::rules()in PHP. - The types (or lack thereof) that the Vue component assumes when receiving
propsor building auseForm().
When someone adds a required field in the backend and forgets to update the frontend (or vice versa), the error only shows up in production, not at compile time. Tools like spatie/laravel-typescript-transformer solve this very well, but they require you to manually annotate every DTO or Resource with PHP attributes.
laravel-inertia-type-sync analyzes your code just as you write it — using Reflection over rules() and AST analysis over toArray() — and generates a single synchronized .d.ts file with a simple Artisan command.
Installation
You can install the package via Composer:
composer require isaiasg3/laravel-inertia-type-sync --dev
Optionally, you can publish the configuration file:
php artisan vendor:publish --tag=inertia-type-sync-config
Check the config/inertia-type-sync.php file to adjust the scanning paths and your models' base namespace if it's different from App\Models.
Usage
Run the following command to generate your types:
php artisan inertia:sync-types
This will generate a file at resources/js/types/generated.d.ts with a TypeScript interface for every FormRequest and JsonResource detected in your project:
export interface StoreUser { name: string; email: string; age?: number | null; role: 'admin' | 'editor' | 'viewer'; items: Array<{ sku: string; quantity: number }>; } export interface UserResource { id: number; name: unknown; is_admin: boolean; created_at: string; }
In a Vue 3 component (Composition API)
Forms with useForm — Inertia's generic type validates every v-model against the generated interface:
<script setup lang="ts"> import { useForm } from '@inertiajs/vue3' import type { StoreUser } from '@/types/generated' const form = useForm<StoreUser>({ name: '', email: '', age: null, role: 'viewer', items: [], }) function submit() { form.post(route('users.store')) } </script> <template> <form @submit.prevent="submit"> <input v-model="form.name" type="text" /> <span v-if="form.errors.name">{{ form.errors.name }}</span> <select v-model="form.role"> <option value="admin">Admin</option> <option value="editor">Editor</option> <option value="viewer">Viewer</option> </select> </form> </template>
Typed Props from a JsonResource:
<script setup lang="ts"> import type { UserResource } from '@/types/generated' const props = defineProps<{ user: UserResource }>() </script> <template> <h1>{{ props.user.name }}</h1> </template>
Keeping the file synced automatically
Integrate the command into your usual workflow by adding it to your composer.json scripts:
{
"scripts": {
"post-autoload-dump": [
"@php artisan inertia:sync-types --ansi"
]
}
}
This way, every time you run composer dump-autoload or install a new package, your TypeScript types will be automatically updated.
How it works under the hood
| PHP Source | Technique | Result |
|---|---|---|
FormRequest::rules() |
PHP Reflection + rule parser | Interface with primitive types, literal unions (in:), and nested arrays (items.*.sku) |
JsonResource::toArray() |
AST Analysis (nikic/php-parser) over the method's source code |
Interface with types inferred from Eloquent $casts, nested resources, and optional when()/whenLoaded() fields |
Known Limitations
- Properties within a
JsonResourcewithout an explicitly declared cast in the corresponding Eloquent model will be generated asunknown. - The AST analysis currently only evaluates a top-level
return [...]statement inside thetoArray()method.
Contributing
Pull Requests are welcome! Please run composer test and composer analyse before submitting your changes.
License
The MIT License (MIT).