PHPackages                             fumeapp/modeltyper - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. fumeapp/modeltyper

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

fumeapp/modeltyper
==================

Generate TypeScript interfaces from Laravel Models

v3.11.0(3w ago)198321.1k↓49.8%32[12 issues](https://github.com/fumeapp/modeltyper/issues)[3 PRs](https://github.com/fumeapp/modeltyper/pulls)MITPHPPHP ^8.2CI passing

Since May 17Pushed 3w ago2 watchersCompare

[ Source](https://github.com/fumeapp/modeltyper)[ Packagist](https://packagist.org/packages/fumeapp/modeltyper)[ RSS](/packages/fumeapp-modeltyper/feed)WikiDiscussions master Synced 2d ago

READMEChangelog (10)Dependencies (44)Versions (89)Used By (0)

Model Typer
===========

[](#model-typer)

[![Github actions](https://github.com/fumeapp/modeltyper/actions/workflows/phpstan.yml/badge.svg)](https://packagist.org/packages/fumeapp/modeltyper)[![Latest Stable Version](https://camo.githubusercontent.com/4c67ea70d9faf97f62749e344a521a52f9d86e37a766862d24b72e2135003c5e/68747470733a2f2f706f7365722e707567782e6f72672f66756d656170702f6d6f64656c74797065722f76)](https://packagist.org/packages/fumeapp/modeltyper)[![Total Downloads](https://camo.githubusercontent.com/9c3ca3f028933b7a96b0b2edeada15b37d319b6b77aa6a645f77a7e223b7ad4f/68747470733a2f2f706f7365722e707567782e6f72672f66756d656170702f6d6f64656c74797065722f646f776e6c6f616473)](https://packagist.org/packages/fumeapp/modeltyper)[![License](https://camo.githubusercontent.com/0883710f06c3d1633153799ad810e1dd57e167d305b6cb955ddbc9c0965ccd75/68747470733a2f2f706f7365722e707567782e6f72672f66756d656170702f6d6f64656c74797065722f6c6963656e7365)](https://choosealicense.com/licenses/mit/)[![PHP Version Require](https://camo.githubusercontent.com/c7d500f72b75dd5bb82d74b991b290ef4ca9ebb9b524f7174fde7478f4e29095/68747470733a2f2f706f7365722e707567782e6f72672f66756d656170702f6d6f64656c74797065722f726571756972652f706870)](https://packagist.org/packages/fumeapp/modeltyper)

 [![](https://camo.githubusercontent.com/ba6475a968fc68e757328930c6d7f9d029f1d963cd27e6c64f1913c8a8716e86/68747470733a2f2f75706c6f61642e77696b696d656469612e6f72672f77696b6970656469612f636f6d6d6f6e732f7468756d622f392f39612f4c61726176656c2e7376672f39363070782d4c61726176656c2e7376672e706e67)](https://laravel.com) [![](https://camo.githubusercontent.com/d0101d203b38db2c2ad7725b8eb9d8bfb2880f1e48a7b6a11a314367a9a9721e/68747470733a2f2f6d69726f2e6d656469756d2e636f6d2f6d61782f3831362f312a6d6e36624f733773365162616f3135504d4e52794f412e706e67)](https://www.typescriptlang.org/)

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.

Why Model Typer over Laravel Wayfinder?
---------------------------------------

[](#why-model-typer-over-laravel-wayfinder)

Laravel's first-party [Wayfinder](https://github.com/laravel/wayfinder) package is adding Eloquent model TypeScript support on its `dev-next` branch. Model Typer has been doing this in production for years. A few things worth knowing before switching:

- **It's already stable.** Model Typer is versioned and running in production across a lot of apps. Wayfinder's model support is still in beta with an explicit note that *"the API is subject (and likely) to change prior to the v1.0.0 release."*
- **No dev dependencies.** Getting Wayfinder's model support requires `composer require laravel/wayfinder:dev-next`, which locks you to an untagged, moving branch instead of a proper release.
- **Models are the whole point.** Wayfinder is primarily a route and controller typing tool. Model support is being added on top of that. Model Typer was built specifically for this use case from the start.
- **More output options.** Model Typer generates columns, accessors, relationships, counts, exists, sums, and custom mappings with a lot of configuration options. Wayfinder's model output is still fairly basic by comparison.

Once Wayfinder hits a stable v1.0.0 with solid model support it will be worth a proper look. Until then, Model Typer is the practical choice.

Upgrade Guide
-------------

[](#upgrade-guide)

Please read the upgrade guide [here](https://github.com/fumeapp/modeltyper/UPGRADE.md)

Installation
------------

[](#installation)

Starting support is for Laravel &gt;=v11.33.0 and PHP v8.2+

Important

For Laravel &gt;=10.43.0 || &lt;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
-----

[](#usage)

You can simply run the following command to generate TypeScript interfaces:

```
php artisan model:typer
```

Or generate TypeScript types instead:

```
php artisan model:typer --use-types
```

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;
    posts: Posts;
    // counts
    teams_count: number;
    posts_count: number;
    // exists
    teams_exists: boolean;
    posts_exists: boolean;
}
export type Users = Array;

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;
    // counts
    users_count: number;
    // exists
    users_exists: boolean;
}
export type Teams = Array;

export interface Post {
    // columns
    id: number;
    user_id: number;
    title: string;
    content: string;
    created_at?: Date;
    updated_at?: Date;
    // mutators
    summary: string;
    // relations
    user: User;
    comments: Comments;
    // counts
    comments_count: number;
    // exists
    comments_exists: boolean;
    // sums
    comments_sum_likes: number | null;
}
export type Posts = Array;

export interface Comment {
    // columns
    id: number;
    post_id: number;
    content: string;
    created_at?: Date;
    updated_at?: Date;
    // mutators
    summary: string;
    // relations
    post: Post;
}
export type Comments = Array;
```

### How does it work?

[](#how-does-it-work)

This command will go through all of your models and make [TypeScript Interfaces](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#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](#optional-arguments)`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](#override-default-mappings--add-new-ones) in the config

### Interfaces vs Types

[](#interfaces-vs-types)

By default, Model Typer generates TypeScript interfaces, but you can also generate type aliases using the `--use-types` option or by setting `use-types` to `true` in the config file.

#### Interfaces (default)

[](#interfaces-default)

```
export interface User {
    id: number;
    name: string;
    email: string;
}
```

#### Types (with --use-types)

[](#types-with---use-types)

```
export type User = {
    id: number;
    name: string;
    email: string;
};
```

**When to use interfaces:**

- When you need extensibility (other interfaces can extend them)
- For object-oriented patterns
- When you want declaration merging capabilities

**When to use types:**

- For more complex type definitions (unions, intersections, etc.)
- When you prefer a more functional approach
- For better performance in some TypeScript compilation scenarios

You can enable types in several ways:

```
# Via CLI option
php artisan model:typer --use-types

# Via config file
# Set 'use-types' => true in config/modeltyper.php

# For a specific model
php artisan model:typer --model=User --use-types
```

### Requirements

[](#requirements)

1. You must have a [return type](https://www.php.net/manual/en/language.types.declarations.php) for your model relationships

```
public function providers(): HasMany // hasMany(Provider::class);
}
```

2. You must have a [return type](https://www.php.net/manual/en/language.types.declarations.php) for your model mutations

```
protected function firstName(): Attribute
{
    return Attribute::make(
        get: fn (string $value): string => ucfirst($value), //  'column_to_sum'
'posts' => 'likes',
];
```

This will generate a corresponding field in the TypeScript interface:

```
export interface User {
...
// Sum of `likes` from related `posts`
posts_sum_likes: number | null;
}
```

This allows you to work with pre-calculated relationship totals directly in your frontend types.

### Custom Interfaces

[](#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

[](#override-default-mappings--add-new-ones)

You can override the default mappings provided by Model Typer or add new ones by [publishing the config](#installation)

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](https://laravel.com/docs/11.x/eloquent-mutators#custom-casts)

```
'custom_mappings' => [
    'App\Casts\YourCustomCast' => 'string | null',
    'binary' => 'Blob',
    'bool' => 'boolean',
    'point' => 'CustomPointInterface',
    'year' => 'string',
],
```

### Declare global

[](#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

[](#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

[](#output-apimetapi-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

[](#enable-all-output-options)

```
artisan model:typer --all
```

Exports both plurals &amp; api-resources. i.e. it is equivalent to:

```
artisan model:typer --plurals --api-resources
```

### For Single Model

[](#for-single-model)

Generate your interfaces for a single model

```
artisan model:typer --model=User
```

### Output as JSON

[](#output-as-json)

Generate your interfaces as JSON

```
artisan model:typer --json
```

### Enum Eloquent Attribute Casting

[](#enum-eloquent-attribute-casting)

Laravel lets you cast [Enums in your models](https://laravel.com/docs/11.x/eloquent-mutators#enum-casting). 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](https://maxheiber.medium.com/alternatives-to-typescript-enums-50e4c16600b1). But you can use `--use-enums` option to use TS Enums instead of Object Literals.

`app/Enums/UserRoleEnum.php`

```
