PHPackages                             brunoscode/laravel-ts-annotations - 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. brunoscode/laravel-ts-annotations

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

brunoscode/laravel-ts-annotations
=================================

Write raw TypeScript types in PHP attributes and generate .ts files with an Artisan command.

v0.2.3(3w ago)0280↑189.3%MITPHPPHP ^8.1CI failing

Since May 10Pushed 3w agoCompare

[ Source](https://github.com/BrunosCode/laravel-ts-annotations)[ Packagist](https://packagist.org/packages/brunoscode/laravel-ts-annotations)[ RSS](/packages/brunoscode-laravel-ts-annotations/feed)WikiDiscussions main Synced 1w ago

READMEChangelog (6)Dependencies (4)Versions (8)Used By (0)

laravel-ts-annotations
======================

[](#laravel-ts-annotations)

Generate TypeScript types from PHP attributes with a single Artisan command. Three annotation styles — raw TypeScript, auto-inferred from class properties, and auto-inferred from enums — cover every common case.

Laravel Boost
-------------

[](#laravel-boost)

This package ships a [Laravel Boost](https://laravel.com/docs/boost) skill. If you use Boost, run:

```
php artisan boost:install
```

and select `brunoscode/laravel-ts-annotations` when prompted. The skill teaches your AI agent how to use `#[TS]`, `#[TSType]`, and `#[TSEnum]` attributes, run `ts:generate`, and manage the generated output.

---

Quick start
-----------

[](#quick-start)

```
// Raw TypeScript — full control
#[TS( [
        app_path('Http'),       // covers Resources, Controllers, Requests, Middleware
        app_path('Data'),       // DTOs annotated with #[TSType]
        app_path('Enums'),      // enums annotated with #[TSEnum]
    ],

    // Output .ts files. The array key is referenced in the `output` param.
    'outputs' => [
        'default' => [
            'path'    => resource_path('js/types/generated.ts'),
            // Lines written verbatim at the top of the generated section on every run.
            // Useful for shared generics like CollectionResource / PaginatedResource.
            'imports' => [
                'export type CollectionResource = { data: T[] };',
                '',
                'export type PaginatedResource = {',
                '    data: T[];',
                '    total: number;',
                '    per_page: number;',
                '    current_page: number;',
                '    last_page: number;',
                '    from: number | null;',
                '    to: number | null;',
                '    first_page_url: string;',
                '    last_page_url: string;',
                '    next_page_url: string | null;',
                '    prev_page_url: string | null;',
                '    path: string;',
                '};',
            ],
        ],
        // 'admin' => [
        //     'path'    => resource_path('js/types/admin.ts'),
        //     'imports' => [],
        // ],
    ],

    // Comment markers that delimit the generated section.
    // Everything outside the markers is preserved on re-generation.
    'markers' => [
        'start' => '// [ts-annotations:start]',
        'end'   => '// [ts-annotations:end]',
    ],

];
```

---

Usage
-----

[](#usage)

### `#[TS]` — raw TypeScript

[](#ts--raw-typescript)

Write any TypeScript verbatim. Use this when you need union types, template literals, generics, or any construct that can't be inferred from PHP types.

Usable on classes and on individual methods. `#[TS]` is repeatable — stack it as many times as needed.

```
use Brunoscode\LaravelTsAnnotations\Attributes\TS;

#[TS(id,
            'name'  => $this->name,
            'email' => $this->email,
            'role'  => $this->role,
        ];
    }
}
```

Keeping the annotation on the Resource rather than the controller means the type and the transformation logic live together. If you tighten `toArray`, you update the `#[TS]` block in the same file.

### 2. Annotate controller methods for Inertia

[](#2-annotate-controller-methods-for-inertia)

The default config injects two generic helpers at the top of every generated file:

```
export type CollectionResource = { data: T[] };

export type PaginatedResource = {
    data: T[];
    total: number;
    per_page: number;
    current_page: number;
    last_page: number;
    from: number | null;
    to: number | null;
    // ...
};
```

Reference them directly in the `#[TS]` attribute on each controller method that renders an Inertia page:

```
use Brunoscode\LaravelTsAnnotations\Attributes\TS;
use Inertia\Inertia;

class UserController extends Controller
{
    #[TS('export type UserIndexProps = { users: PaginatedResource }')]
    public function index(): \Inertia\Response
    {
        return Inertia::render('Users/Index', [
            'users' => UserResource::collection(User::paginate()),
        ]);
    }

    #[TS('export type UserListProps = { users: CollectionResource }')]
    public function list(): \Inertia\Response
    {
        return Inertia::render('Users/List', [
            'users' => UserResource::collection(User::all()),
        ]);
    }

    #[TS('export type UserShowProps = { user: UserResource }')]
    public function show(User $user): \Inertia\Response
    {
        return Inertia::render('Users/Show', [
            'user' => new UserResource($user),
        ]);
    }
}
```

### 3. Generated TypeScript

[](#3-generated-typescript)

```
// resources/js/types/generated.ts

// [ts-annotations:start]
// ⚠️  Auto-generated — do not edit between these comments.

export type CollectionResource = { data: T[] };

export type PaginatedResource = {
    data: T[];
    total: number;
    per_page: number;
    current_page: number;
    last_page: number;
    from: number | null;
    to: number | null;
    first_page_url: string;
    last_page_url: string;
    next_page_url: string | null;
    prev_page_url: string | null;
    path: string;
};

// --- App\Http\Resources\UserResource ---
export type UserResource = {
    id: number;
    name: string;
    email: string;
    role: 'admin' | 'editor' | 'viewer';
}

// --- App\Http\Controllers\UserController ---
export type UserIndexProps = { users: PaginatedResource }
export type UserListProps  = { users: CollectionResource }
export type UserShowProps  = { user: UserResource }
// [ts-annotations:end]
```

### 4. Consume in an Inertia component

[](#4-consume-in-an-inertia-component)

```

import type { UserIndexProps } from '@/types/generated'

const props = defineProps()
// props.users.data        → UserResource[]
// props.users.total       → number
// props.users.current_page → number

```

```

import type { UserShowProps } from '@/types/generated'

const props = defineProps()
// props.user.id, props.user.name, props.user.role — fully typed

```

> `CollectionResource` and `PaginatedResource` are injected via the `imports` key in `config/ts-annotations.php`. Customise them there or add any other shared helpers your app needs.

---

Ordering in the output file
---------------------------

[](#ordering-in-the-output-file)

Within each output file, entries are written in this order:

1. Class-level `#[TS]` attributes, in file-scan order
2. `#[TSEnum]` entries, in file-scan order
3. `#[TSType]` entries, in file-scan order
4. Method-level `#[TS]` attributes, sorted by line number within each class

Each entry is preceded by a source comment:

```
// --- App\Http\Resources\UserResource ---
export type UserResponse = { ... }

// --- App\Enums\Status ---
export enum Status { ... }

// --- App\Data\UserData ---
export type UserData = { ... }
```

---

File preservation
-----------------

[](#file-preservation)

The generator only touches the section between the two marker comments. Everything outside the markers — manual imports, custom types, hand-written utilities — is left untouched on every run.

```
// My manual import — never overwritten
import type { CustomHelper } from './helpers'

// [ts-annotations:start]
// ⚠️  Auto-generated — do not edit between these comments.
// Generated at: 2026-05-10 12:00:00

// --- App\Http\Resources\UserResource ---
export type UserResponse = { ... }
// [ts-annotations:end]

// My local type — never overwritten
export type LocalState = 'idle' | 'loading' | 'error'
```

If a file doesn't exist yet, it is created from scratch. If it exists but has no markers, the generated block is appended at the end.

---

Roadmap
-------

[](#roadmap)

- `--watch` flag for automatic regeneration on file change

---

Testing
-------

[](#testing)

```
composer install
vendor/bin/phpunit
```

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance95

Actively maintained with recent releases

Popularity17

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity37

Early-stage or recently created project

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~1 days

Total

6

Last Release

22d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/a76a0e4fa1309d69450b1e860c42a4cc7973277bda7a3b8aac15b14a1f78e408?d=identicon)[BrunosCode](/maintainers/BrunosCode)

---

Top Contributors

[![BrunosCode](https://avatars.githubusercontent.com/u/78606186?v=4)](https://github.com/BrunosCode "BrunosCode (10 commits)")

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/brunoscode-laravel-ts-annotations/health.svg)

```
[![Health](https://phpackages.com/badges/brunoscode-laravel-ts-annotations/health.svg)](https://phpackages.com/packages/brunoscode-laravel-ts-annotations)
```

###  Alternatives

[laravel/ai

The official AI SDK for Laravel.

9782.1M153](/packages/laravel-ai)[spatie/laravel-export

Create a static site bundle from a Laravel app

670139.5k6](/packages/spatie-laravel-export)[tallstackui/tallstackui

TallStackUI is a powerful suite of Blade components that elevate your workflow of Livewire applications.

719160.4k12](/packages/tallstackui-tallstackui)[zidbih/laravel-deadlock

Make temporary Laravel workarounds expire and fail CI when ignored.

954.0k](/packages/zidbih-laravel-deadlock)[interaction-design-foundation/laravel-geoip

Support for multiple Geographical Location services.

19253.0k3](/packages/interaction-design-foundation-laravel-geoip)[tomshaw/electricgrid

A feature-rich Livewire package designed for projects that require dynamic, interactive data tables.

119.2k](/packages/tomshaw-electricgrid)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
