fumeapp / modeltyper
Generate TypeScript interfaces from Laravel Models
Installs: 154 175
Dependents: 0
Suggesters: 0
Security: 0
Stars: 136
Watchers: 5
Forks: 16
Open Issues: 7
Requires
- php: ^8.2
- illuminate/console: ^11.33.0
- illuminate/database: ^11.33.0
- illuminate/support: ^11.33.0
Requires (Dev)
- consolidation/robo: ^5.1.0
- larastan/larastan: ^3.0.2
- laravel/pint: ^1.18.3
- orchestra/testbench: ^9.6.1
- phpstan/phpstan-deprecation-rules: ^2.0.1
- phpunit/phpunit: ^11.4.4
- totten/lurkerlite: ^1.3
Conflicts
- laravel/framework: <11.33.0
- nesbot/carbon: <3.5.0
- dev-master
- v3.0.1
- v3.0.0
- v2.8.2
- v2.8.1
- v2.8.0
- v2.7.0
- v2.6.1
- v2.6.0
- v2.5.0
- v2.4.0
- v2.3.0
- v2.2.9
- v2.2.8
- v2.2.7
- v2.2.6
- v2.2.5
- v2.2.4
- v2.2.3
- v2.2.2
- v2.2.1
- v2.2.0
- v2.1.2
- v2.1.1
- v2.1.0
- v2.0.4
- v2.0.3
- v2.0.2
- v2.0.1
- v2.0.0
- v1.2.1
- v1.2.0
- v1.1.6
- v1.1.5
- v1.1.4
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.29
- v1.0.28
- v1.0.27
- v1.0.26
- v1.0.25
- v1.0.24
- v1.0.22
- v1.0.21
- v1.0.20
- v1.0.19
- v1.0.18
- v1.0.17
- v1.0.16
- v1.0.15
- v1.0.14
- v1.0.13
- v1.0.12
- v1.0.11
- v1.0.10
- v1.0.9
- v1.0.8
- v1.0.7
- v1.0.6
- v1.0.5
- v1.0.4
- v1.0.3
- v1.0.2
- v1.0.1
- v1.0.0
- dev-ops/stale
- dev-ops/devcontainer
- dev-bugfix/check-class
- dev-tcampbPPU-patch-1
- dev-feat/tests
- dev-clean-up-dbl-error
- dev-faeture/relation-counts
- dev-tanner/dev
This package is auto-updated.
Last update: 2024-12-16 17:54:46 UTC
README
Model Typer is a powerful tool designed for developers working with Laravel and TypeScript. Its primary purpose is to simplify the generation of TypeScript interfaces from Laravel models, enhancing type safety and consistency in your applications.
Upgrade Guide
Please read the upgrade guide here
Installation
Starting support is for Laravel >=v11.33.0 and PHP v8.2+
Important
For Laravel >=10.43.0 || <11.33.0, use v2 instead
Require this package with composer using the following command:
composer require --dev fumeapp/modeltyper
Optionally, you can publish the config file using the following command:
php artisan vendor:publish --provider="FumeApp\ModelTyper\ModelTyperServiceProvider" --tag=config
Usage
You can simply run the following command to generate TypeScript interfaces:
php artisan model:typer
The output is an accurate, type-safe representation of Laravel models in TypeScript, such as:
export interface User { // columns id: number; email: string; name: string; created_at?: Date; updated_at?: Date; // mutators first_name: string; initials: string; // relations teams: Teams; } export type Users = Array<User>; export interface Team { // columns id: number; name: string; logo: string; created_at?: Date; updated_at?: Date; // mutators initials: string; slug: string; url: string; // relations users: Users; } export type Teams = Array<Team>;
How does it work?
This command will go through all of your models and make TypeScript Interfaces based on the database columns, mutators, and relationships.
You can then pipe the output into your preferred ???.d.ts
, or set the optional argument output-file
to generate it
Tip
To view the current mappings that are being used, use the following command:
php artisan model:typer-mappings
These mappings can be extended or overridden in the config
Requirements
- You must have a return type for your model relationships
public function providers(): HasMany // <- this { return $this->hasMany(Provider::class); }
- You must have a return type for your model mutations
protected function firstName(): Attribute { return Attribute::make( get: fn (string $value): string => ucfirst($value), // <- this ); }
Optional Arguments
- output-file : Echo the definitions into a file
Additional Options
- --model= : Generate typescript interfaces for a specific model
- --global : Generate typescript interfaces in a global namespace named models
- --json : Output the result as json
- --use-enums : Use typescript enums instead of object literals
- --plurals : Output model plurals
- --no-relations : Do not include relations
- --optional-relations : Make relations optional fields on the model type
- --no-hidden : Do not include hidden model attributes
- --timestamps-date : Output timestamps as a Date object type
- --optional-nullables : Output nullable attributes as optional fields
- --api-resources : Output api.MetApi interfaces
- --fillables : Output model fillables
- --fillable-suffix= : Appends to fillables
- --ignore-config : Ignore options set in config
Custom Interfaces
If you have custom interfaces you are using for your models you can specify them in a reserved interfaces
array
For example for a custom Point
interface in a Location
model you can put this in the model
public array $interfaces = [ 'coordinate' => [ 'import' => "@/types/api", 'type' => 'Point', ], ];
And it will generate:
import { Point } from "@/types/api"; export interface Location { // override coordinate: Point; }
This will override all columns, mutators and relationships
You can also specify an interface is nullable:
public array $interfaces = [ 'choices' => [ 'import' => '@/types/api', 'type' => 'ChoicesWithPivot', 'nullable' => true, ], ];
You can also choose to leave off the import and just use the type:
public array $interfaces = [ 'choices' => [ 'type' => "'good' | 'bad'", ], ];
And it should generate:
export interface Location { // columns choices: "good" | "bad"; }
Using the custom interface is also a good place to append any additional properties you want to add to the interface.
For example, if your interface keeps some additional state in something like Vuex, you can add it to the interfaces:
public array $interfaces = [ 'state' => [ 'type' => "found' | 'not_found' | 'searching' | 'reset'", ], ];
This will generate:
export interface Location { // ... // overrides state: "found" | "not_found" | "searching" | "reset"; // ... }
Override default mappings / add new ones
You can override the default mappings provided by Model Typer or add new ones by publishing the config
Then inside custom_mappings
add the Laravel type as the key and assign the TypeScript type as its value
You can also add mappings for your Custom Casts
'custom_mappings' => [ 'App\Casts\YourCustomCast' => 'string | null', 'binary' => 'Blob', 'bool' => 'boolean', 'point' => 'CustomPointInterface', 'year' => 'string', ],
Declare global
Generate your interfaces in a global namespace named model
artisan model:typer --global
export {} declare global { export namespace models { export interface Provider { // columns id: number user_id: number avatar?: string ...
Output plural interfaces for collections
artisan model:typer --plurals
Exports for example, when a User
model exists:
export type Users = User[]
Output Api.MetApi* resources
artisan model:typer --api-resources
Exports:
export interface UserResults extends api.MetApiResults { data: Users } export interface UserResult extends api.MetApiResults { data: User } export interface UserMetApiData extends api.MetApiData { data: User } export interface UserResponse extends api.MetApiResponse { data: UserMetApiData }
Enable all output options
artisan model:typer --all
Exports both plurals & api-resources. i.e. it is equivalent to:
artisan model:typer --plurals --api-resources
For Single Model
Generate your interfaces for a single model
artisan model:typer --model=User
Output as JSON
Generate your interfaces as JSON
artisan model:typer --json
Enum Eloquent Attribute Casting
Laravel lets you cast Enums in your models. This will get detected and bring in your enum class with your comments:
Note
ModelTyper uses Object Literals by default instead of TS Enums for opinionated reasons. But you can use --use-enums
option to use TS Enums instead of Object Literals.
app/Enums/UserRoleEnum.php
<?php namespace App\Enums; /** * @property ADMIN - Can do anything * @property USER - Standard read-only */ enum UserRoleEnum: string { case ADMIN = 'admin'; case USER = 'user'; }
Then inside our User model
app/Models/User.php
protected $casts = [ 'role' => App\Enums\UserRoleEnum::class, ];
Now our ModelTyper output will look like the following:
const UserRoleEnum = { /** Can do anything */ ADMIN: 'admin', /** Standard read-only */ USER: 'user', } export type UseRoleEnum = typeof UseRoleEnum[keyof typeof UserRoleEnum] export interface User { ... role: UserRoleEnum ... }
Note
Notice how the comments are found and parsed - they must follow the specified format