Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Typescript Transformer Laravel Package

spatie/laravel-typescript-transformer

Convert PHP classes, enums, and more into TypeScript types automatically. Uses attributes to generate TS from your Laravel code, supports nullable and complex types, generics, and even TypeScript functions—keeping your backend and frontend types in sync.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require spatie/laravel-typescript-transformer
    

    Publish the service provider and config (if needed):

    php artisan vendor:publish --provider="Spatie\LaravelTypeScriptTransformer\TypeScriptTransformerApplicationServiceProvider"
    
  2. Basic Usage: Annotate a PHP class with #[TypeScript] to generate a TypeScript type:

    // app/Models/User.php
    use Spatie\LaravelTypeScriptTransformer\Attributes\TypeScript;
    
    #[TypeScript]
    class User {
        public int $id;
        public string $name;
        public ?string $address;
    }
    

    Run the transformer:

    php artisan typescript:transform
    

    Output (in resources/js/generated.d.ts by default):

    export type User = {
        id: number;
        name: string;
        address: string | null;
    }
    
  3. Watch Mode (for development):

    php artisan typescript:watch
    

    Automatically regenerates TypeScript on file changes.


First Use Case: API Response Types

Transform a DTO or Eloquent model to TypeScript for frontend API responses:

#[TypeScript]
class Post {
    public int $id;
    public string $title;
    public Carbon $publishedAt;
    public ?string $excerpt;
    public Collection<Comment> $comments;
}

Generates:

export type Post = {
    id: number;
    title: string;
    publishedAt: string; // Carbon auto-converts to string
    excerpt: string | null;
    comments: Comment[];
}

Implementation Patterns

1. Class-Level Transformation

  • Annotations: Use #[TypeScript] on classes to generate types.
  • Exclusions: Skip properties with #[Ignore] or #[TypeScript(ignore: true)].
  • Custom Naming: Override TypeScript names with #[TypeScript(name: 'CustomName')].

2. Controller Actions

Generate typed route helpers and controller actions:

#[TypeScript]
class PostController {
    #[TypeScript]
    public function index(Request $request): Collection<Post> { ... }

    #[TypeScript]
    public function store(StorePostRequest $request): Post { ... }
}

Output:

// Generated route helper
declare function route(name: 'posts.index', parameters?: { [key: string]: unknown }): string;

// Controller types
export type PostController = {
    index: (request: Request) => Post[];
    store: (request: StorePostRequest) => Post;
};

3. Enums

Transform PHP enums to TypeScript unions:

enum Status: string {
    case PUBLISHED = 'published';
    case DRAFT = 'draft';
}

Output:

export type Status = 'published' | 'draft';

4. Laravel-Specific Types

  • Collections: Auto-transform Collection<T> to T[].
  • Requests: Generate types for Illuminate\Http\Request and form requests.
  • Route Parameters: Infer types from route definitions (e.g., {id: number}).

5. Integration with laravel-data

If using spatie/laravel-data, the transformer respects name_mapping_strategy.output:

// config/data.php
'name_mapping_strategy' => [
    'output' => \Spatie\Data\Mapping\SnakeCaseMapper::class,
],

Output:

export type User = {
    first_name: string; // Instead of camelCase
    last_name: string;
}

Workflows

  1. Development:

    • Use typescript:watch to auto-generate types during development.
    • Place generated files in resources/js/generated.d.ts (or custom path).
  2. Production:

    • Run typescript:transform in your build script (e.g., npm run dev).
    • Commit generated files to version control (or use a build step).
  3. Testing:

    • Mock the transformer in tests:
      $transformer = app(TypeScriptTransformer::class);
      $result = $transformer->transform(new User());
      

Integration Tips

  • Frontend Frameworks:

    • Use generated types with Inertia.js, Vue, or React for full type safety.
    • Example with Inertia:
      router.visit('/posts', { data: { posts: Post[] } });
      
  • API Clients:

    • Generate types for API responses and requests (e.g., with Axios or Fetch).
  • Custom Transformers:

    • Extend Spatie\TypeScriptTransformer\Transformers\Transformer for custom logic:
      class CustomTransformer extends Transformer {
          public function transformProperty(PropertyNode $property): string {
              return match ($property->type) {
                  'App\Models\User' => 'UserType',
                  default => parent::transformProperty($property),
              };
          }
      }
      
    • Register in the service provider:
      $this->app->bind(
          TypeScriptTransformer::class,
          fn () => new TypeScriptTransformer([new CustomTransformer()])
      );
      

Gotchas and Tips

Pitfalls

  1. Circular Dependencies:

    • Avoid circular references between classes (e.g., User referencing Post which references User).
    • Fix: Use #[TypeScript(ignore: true)] or refactor.
  2. Carbon/DateTime:

    • By default, Carbon instances convert to string. Override in a custom transformer:
      public function transformProperty(PropertyNode $property): string {
          if ($property->type === Carbon::class) {
              return 'Date';
          }
          return parent::transformProperty($property);
      }
      
  3. Route Watcher Issues:

    • If typescript:watch crashes with "null byte" errors, ensure no RouteFilter uses protected properties.
    • Fix: Update to v3.1.0+ (includes base64 encoding fix).
  4. Absolute Paths:

    • Avoid passing absolute paths to GlobalNamespaceWriter (e.g., /home/user/project/file.d.ts).
    • Fix: Use relative paths (e.g., generated.d.ts).
  5. Laravel-Data Conflicts:

    • If laravel-data and the transformer conflict on naming, explicitly set:
      #[TypeScript(name: 'UserDto')]
      class User extends Data {}
      

Debugging

  1. Verbose Output: Enable debug mode in the service provider:

    $this->app->singleton(TypeScriptTransformer::class, fn () => new TypeScriptTransformer([
        new DtoTransformer(),
        new CollectionTransformer(),
        // ...
    ], debug: true));
    
  2. Check Generated Files:

    • Inspect resources/js/generated.d.ts for errors.
    • Use php artisan typescript:transform --verbose for detailed logs.
  3. Common Errors:

    • Class not found: Ensure the class is autoloaded (e.g., composer dump-autoload).
    • TypeScriptTransformer not bound: Register the service provider in config/app.php.

Tips

  1. Exclude Files: Use .typescript-transformer-ignore to skip files/directories:

    # .typescript-transformer-ignore
    app/Models/IgnoredModel.php
    
  2. Custom Output Path: Configure in the service provider:

    $this->app->singleton(TypeScriptTransformer::class, fn () => new TypeScriptTransformer(
        transformers: [],
        outputPath: __DIR__.'/../resources/js/types',
    ));
    
  3. Format Generated Code: Integrate with Prettier or ESLint to auto-format:

    $this->app->singleton(TypeScriptTransformer::class, fn () => new TypeScriptTransformer(
        transformers: [],
        writer: new GlobalNamespaceWriter(
            outputPath: __DIR__.'/../resources/js/types',
            formatter: new PrettierFormatter(),
        ),
    ));
    
  4. Partial Transforms: Generate types for specific classes only:

    php artisan typescript:transform --class="App\Models\User,App\Models/Post"
    
  5. Performance:

    • Cache generated files in production (e.g., via laravel-mix or vite).
    • Exclude large classes from transformation if unused in the frontend.
  6. Testing:

    • Mock the transformer in PHPUnit:
      $transformer = Mockery::mock(TypeScriptTransformer::class);
      $transformer->shouldReceive('transform')
          ->once()
          ->andReturn('export type User = { id: number }');
      $this->app->instance(TypeScriptTransformer::class, $transformer);
      
  7. HTTP Methods Filtering: Customize controller action generation with httpMethodsPriority:

    $this->app->singleton(Lar
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
codraw/framework-extra-bundle
codraw/messenger
codraw/security
codraw/mailer
codraw/contracts
codraw/profiling
codraw/dependency-injection
codraw/tester
codraw/core
nexmo/api-specification
capell-app/block-library
axium/identity
cetria/laravel-dummy-models
cetria/reflection-helper
agropredict/sso-auth-bundle
evolvestudio/spam-protection
datacore/hub-sdk
develia/commons
cuci/prototurk-sdk
cuci/prototurk-sdk-symfony