Generate TypeScript interfaces from Laravel Models
composer require --dev fumeapp/modeltyper
php artisan model:typer
will output
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>
This command will go through all of your models and make TypeScript Interfaces based on the columns, mutators, and relationships. You can then pipe the output into your preferred ???.d.ts
Starting support is for Laravel v8+ and PHP v8+
- 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
public function getFirstNameAttribute(): string // <- this
{
return explode(' ', $this->name)[0];
}
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' => [
'name' => 'Point',
'import' => "@/types/api",
],
];
And it should generate:
import { Point } from '@/types/api'
export interface Location {
// columns
coordinate: Point
}
This will override all columns, mutators and relationships
You can also specify an interface is nullable:
public array $interfaces = [
'choices' => [
'name' => 'ChoicesWithPivot',
'import' => '@/types/api',
'nullable' => true,
],
];
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
...
Laravel now has a very different way of specifying accessors and mutators.
In order to tell modeltyper the types of your attributes - be sure to add the type the attribute returns:
/**
* Determine if the user is a captain of a team
*
* @return Attribute
*/
public function isCaptain(): Attribute
{
return Attribute::make(
get: fn (): bool => $this->teams[0]->pivot->captain ?? false,
);
}
This will generate something like:
export interface User {
// columns
id: number
email: string
name?: string
created_at?: Date
updated_at?: Date
// mutators
is_captain: boolean
// relations
teams: TeamUsers
}
Laravel now lets you cast Enums in your models. This will get detected and bring in your enum class with your comments:
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
...
}
ModelTyper uses Object Literals instead of TS Enums for opinionated reasons
Notice how the comments are found and parsed - they must follow the specified format