nolanos/laravel-model-typescript-transfomer
composer require nolanos/laravel-model-typescript-transformer
Nolanos\LaravelModelTypescriptTransformer\ModelTypeScriptCollector to collectors and Nolanos\LaravelModelTypescriptTransformer\ModelTransformer to transformers in config/typescript-transformer.php.php artisan typescript:generate
Outputs TypeScript definitions to resources/js/types/models.d.ts (default).Generate TypeScript interfaces for a User model with a posts() relationship:
// app/Models/User.php
class User extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
After running typescript:generate, you’ll get:
interface User {
id: number;
name: string;
email: string;
// ...other columns
posts: Post[];
}
--only flag to regenerate specific models:
php artisan typescript:generate --only=User,Post
config/typescript-transformer.php:
'output_path' => 'resources/js/types/custom-models.d.ts',
ModelTransformer::class to ignored_transformers in config to skip specific models.ModelTransformer to include searchableAs:
// app/Providers/AppServiceProvider.php
$transformer->extend('User', function ($model) {
return collect($model->getFillable())
->merge(['searchableAs' => $model->searchableAs]);
});
// config/typescript-transformer.php
'transformers' => [
\Nolanos\LaravelModelTypescriptTransformer\ModelTransformer::class,
\App\Transformers\PolymorphicTransformer::class,
],
ModelTransformer to include toArray() fields.// app/Transformers/CustomModelTransformer.php
namespace App\Transformers;
use Nolanos\LaravelModelTypescriptTransformer\ModelTransformer;
class CustomModelTransformer extends ModelTransformer
{
protected function getTypeForColumn($column, $model)
{
if ($column === 'status') {
return 'UserStatus'; // Custom enum/type
}
return parent::getTypeForColumn($column, $model);
}
}
Hidden/Visible Attributes:
The transformer respects $hidden and $visible properties, but casts are ignored. Manually override types for casted attributes (e.g., date → string):
// Override in config or custom transformer
$transformer->extend('User', function ($model) {
return collect($model->getFillable())
->mapWithKeys(fn ($value, $key) => [
'created_at' => 'string', // Force string for date
'password' => 'string',
]);
});
Relationships:
Model[]. Customize via ModelTransformer::extend():
$transformer->extend('User', function ($model) {
return collect($model->getRelations())
->mapWithKeys(fn ($relation) => [
'roles' => 'Role[]', // Override default
]);
});
ModelTransformer to handle morphTo/morphWith.Database Schema Changes: Regenerate types after schema changes:
php artisan typescript:generate --force
The --force flag overwrites existing files.
Circular Dependencies:
If models reference each other (e.g., User has posts, Post has user), TypeScript may complain. Use interfaces or type aliases in your .d.ts:
// resources/js/types/models.d.ts
interface User {
posts: Post[];
}
interface Post {
user: User;
}
php artisan typescript:generate --verbose
ModelTypeScriptCollector to verify which models are being processed. Override getModels() if needed:
// app/Providers/AppServiceProvider.php
$collector->extend('models', function () {
return [User::class, Post::class]; // Custom list
});
getTypeForColumn() in a custom transformer for non-standard DB types (e.g., json → Record<string, unknown>).ModelTransformer::extend() to modify types before/after generation:
$transformer->extend('User', function ($model) {
return collect($model->getFillable())
->merge(['avatar' => 'string | null']); // Custom union type
});
typescript.generated event to post-process files:
// app/Providers/AppServiceProvider.php
event(new \Spatie\LaravelTypeScriptTransformer\Events\TypeScriptGenerated(
$path,
$content
));
output_path in config/typescript-transformer.php is writable. Default: resources/js/types/models.d.ts.App\Models\User), the output will be AppModelsUser. Override via getInterfaceName() in a custom transformer:
protected function getInterfaceName($model)
{
return 'User'; // Flatten namespace
}
How can I help you explore Laravel packages today?