spatie/laravel-typescript-transformer
Generate TypeScript types from your Laravel/PHP code. Convert classes, DTOs and enums (with attributes) into accurate TS definitions, supporting nullable fields, complex/generic types, and even TypeScript function generation via a simple CLI/workflow.
Installation:
composer require spatie/laravel-typescript-transformer --dev
Publish the config (optional):
php artisan vendor:publish --provider="Spatie\LaravelTypeScriptTransformer\TypeScriptTransformerServiceProvider"
First Use Case:
Annotate a controller with #[TypeScript] and run the transformer:
php artisan typescript:transform
Output will be generated in resources/js/types/ (configurable).
Example: resources/js/types/controllers/SomeController.d.ts
Key Files:
config/typescript-transformer.php: Output paths, naming conventions, and new http_methods_priority (3.2.0+).app/Http/Controllers/SomeController.php: Example controller to transform.resources/js/types/controllers/SomeController.d.ts: Generated output with narrowed method types (e.g., 'get' | 'post' instead of string).Controller Transformation (3.2.0+):
#[TypeScript]
class SomeController extends Controller {
public function index() { ... }
public function store(Request $request) { ... }
public function update(Request $request, $id) { ... }
}
Output (3.2.0):
export type SomeController = {
index: () => Promise<void>;
store: (request: Request) => Promise<void>;
update: (request: Request, id: string) => Promise<void>;
};
export type RouteDefinition = {
path: string;
method: 'get' | 'post' | 'put' | 'patch' | 'delete'; // Narrowed union (3.2.0)
controller: SomeController;
};
HTTP Method Filtering (3.2.0):
Configure http_methods_priority in typescript-transformer.php:
'http_methods_priority' => ['get', 'post', 'put', 'patch', 'delete'],
HEAD/OPTIONS (commonly unused in SPAs).'head' to include HEAD methods.Inertia.js Integration (3.2.0):
router.visit(SomeController.update(), { data: { id: '1' } });
Now works without type errors due to narrowed method types.
Partial Transforms:
Use #[TypeScript(only: ['index', 'store'])] to transform specific methods.
Dynamic Route Generation:
Hook into typescript.transformed to extend route definitions:
TypeScriptTransformer::macro('postProcess', function ($output) {
return preg_replace('/method: string/', 'method: \'get\' | \'post\'', $output);
});
API Response Types: Transform DTOs for API contracts alongside controllers:
#[TypeScript]
class UserResponse {
public User $data;
public array<int, Meta> $meta;
}
Custom HTTP Methods:
Override defaults to include HEAD:
'http_methods_priority' => ['get', 'head', 'post'],
Breaking Change in 3.2.0:
method: string → method: 'get' | 'post' | 'put' | 'patch' | 'delete' (narrowed union).
string (e.g., older Inertia versions) may fail.postProcess macro to revert if needed.Missing HTTP Methods:
HEAD/OPTIONS are excluded by default.http_methods_priority in config to include 'head'.Circular References:
#[TypeScript(ignore: true)] or manually resolve dependencies.Laravel-Specific Types:
Request, Response, or Carbon may not map cleanly.TypeScriptTransformer::macro('laravelType', function ($type) {
return match ($type) {
'Illuminate\Http\Request' => 'Request',
'Illuminate\Support\Carbon' => 'Date',
default => $type,
};
});
Dry Run: Preview output without writing files:
php artisan typescript:transform --dry-run
Verbose Logging: Enable debug mode in config:
'debug' => true,
Manual Overrides: Exclude problematic controllers:
#[TypeScript(ignore: true)]
class ComplexController { ... }
Custom HTTP Methods:
Extend http_methods_priority dynamically:
config(['typescript-transformer.http_methods_priority' => ['get', 'post', 'head']]);
Post-Processing:
Modify generated types via typescript.transformed:
event(new TypeScriptTransformed($className, $output));
Webpack/Vite Integration:
Ensure tsconfig.json includes:
{
"compilerOptions": {
"typeRoots": ["./resources/js/types"]
}
}
Inertia.js Compatibility:
For libraries requiring string methods, use a macro:
TypeScriptTransformer::macro('inertiaCompatible', function ($output) {
return str_replace(
"'get' | 'post' | 'put' | 'patch' | 'delete'",
"string",
$output
);
});
How can I help you explore Laravel packages today?