jpnut / laravel-typescript-codegen
Typescript Code Generation for Laravel
Requires
- php: ^7.4|^8.0
- ext-json: *
- illuminate/contracts: ^8.0
- illuminate/routing: ^8.0
- illuminate/support: ^8.0
- phpdocumentor/reflection-docblock: ^5.0
Requires (Dev)
- orchestra/testbench: ^6.0
- phpunit/phpunit: ^9.1
README
This package allows you to generate a typescript schema by getting type information from your Laravel routes and controllers.
For example, you can turn this:
// routes/web.php ... Route::delete('/foo/{foo}', FooController::class.'@delete')->name('foo.delete'); ...
// App\Controllers\Web\FooController ... /** * @code-gen */ public function delete(Request $request, Foo $foo): bool { return $foo->delete(); } ...
into this:
export const fooDeleteRequest = (foo: string, options?: RequestInit) => request<bool>(`https://localhost/foo/${foo}`, { method: `DELETE`, ...options });
Installation
You can install the package via composer:
composer require jpnut/laravel-typescript-codegen
Usage
To generate the schema, use the generate command with the desired file location as the only argument:
php artisan code-gen:generate schema.ts
In order to generate a function for a given route, you must add the @code-gen
tag to the doc-block of the controller method which is used by the route.
... /** * @code-gen */ public function delete(Request $request, Foo $foo): bool ...
This will attempt to get return type information from the doc-block @return
tag, or, if that is not present, the return type of the method. If there is no type information present, the return type defaults to any
.
Scalar types (e.g. string
, int
, etc.) will be transformed into the appropriate typescript scalar (e.g. string
, number
, etc.). If the method returns a class, then the class will be transformed into an interface containing all the public properties of the class.
For example, given the following class:
// App\Objects\User ... class User { public string $name; public int $age; /** * App\Object\Token[] */ public array $tokens; }
the generated interfaces will look as follows:
interface User { name: string; age: number; tokens: Token[]; } interface Token { ... }
Working with Requests
Generating types for request properties (e.g. the request body) is slightly more involved. The request must implement JPNut\CodeGen\Contracts\CodeGenRequest
, and should be resolvable by Laravel's Service Container (typically this means extending Illuminate\Foundation\Http\FormRequest
). You should then create and tag methods which create a mapping from the request to the desired type. For example:
// App\Requests\UpdateUserRequest ... class UpdateUserRequest extends FormRequest { /** * @code-gen-property body */ public function data(): UpdateUserParams { return new UpdateUserParams([ 'name' => $this->input('name'), 'age' => $this->input('age'), ]); } }
// App\Objects\UpdateUserParams ... class UpdateUserParams { ... public ?string $name; public ?int $age; }
In this case the generated interfaces and methods would look similar to the following
interface UpdateUserRequest { body: UpdateUserParams; } interface UpdateUserParams { name?: string | null; age?: number | null; } export const fooUpdateRequest = ({ body }: UpdateUserRequest, foo: string, options?: RequestInit) => request<any>(`http://localhost/foo/${foo}`, { method: 'PUT', body: JSON.stringify(body), ...options });
Note that the body parameter is serialised for the request automatically. You can change this or serialise other properties by modifying the request_properties
config property.
Ignoring Properties
You may have public properties which you do not wish to generate types for. Properties can be ignored by including the @code-gen-ignore
tag:
class Foo { /** * @code-gen-ignore */ public string $ignored_property; }
Using a custom stub
By default, this package will generate the schema using a stub. This stub contains a request
method which is a wrapper for the fetch api and returns a promise containing the deserialised response data.
If you wish to customise this stub, you should first publish the config file
php artisan vendor:publish --provider="JPNut\CodeGen\CodeGenServiceProvider" --tag="config"
This will also create a copy of the default stub file located at resource_path('stubs/typescript-codegen-schema.stub')
. This file will then be used to generate the schema.
Using a custom interface/method writer
You may wish to change the generated output for interfaces and methods. For example, you might decide to rename the request
method within the stub. In this case, you should create your own Interface or Method writer and replace the included writer(s) with your version in the writers
array of the config. The writer(s) should implement the JPNut\CodeGen\Contracts\InterfaceWriterContract
and JPNut\CodeGen\Contracts\MethodWriterContract
interfaces respectively.
Override class resolution
You may wish to resolve certain classes into literals. This is particularly useful for classes which are not part of your codebase. To achieve this, include the class in the default_literals
array of the config. The class name should be the key, and the value should be an array of php literal types (e.g. string, int etc.)
For example, by default this package converts the Carbon\Carbon
class into a string. This is necessary since the return type of the jsonSerialize
method of the class is string|array
.
Testing
vendor/bin/phpunit
License
The MIT License (MIT). Please see License File for more information.