From 71502de178d63d8c5056c7d372dfc548243c3ba9 Mon Sep 17 00:00:00 2001 From: Alex McCabe Date: Mon, 18 Dec 2023 11:32:26 +0000 Subject: [PATCH] Improve generated types to include optional types --- src/Output/Types.php | 8 +++++++- src/js/index.d.ts | 21 ++++++++++++++++++--- tests/Unit/CommandRouteGeneratorTest.php | 6 ++++-- tests/fixtures/ziggy-7.d.ts | 6 ++++-- tests/fixtures/ziggy.d.ts | 7 ++++++- 5 files changed, 39 insertions(+), 9 deletions(-) diff --git a/src/Output/Types.php b/src/Output/Types.php index 45ba303c..d8900e7e 100644 --- a/src/Output/Types.php +++ b/src/Output/Types.php @@ -3,6 +3,7 @@ namespace Tightenco\Ziggy\Output; use Illuminate\Support\Arr; +use Str; use Stringable; use Tightenco\Ziggy\Ziggy; @@ -15,13 +16,18 @@ public function __construct(Ziggy $ziggy) $this->ziggy = $ziggy; } + private function isParamOptional(string $uri, string $param): bool + { + return Str::contains($uri, "{$param}?"); + } + public function __toString(): string { $routes = collect($this->ziggy->toArray()['routes'])->map(function ($route) { return collect($route['parameters'] ?? [])->map(function ($param) use ($route) { return Arr::has($route, "bindings.{$param}") ? ['name' => $param, 'binding' => $route['bindings'][$param]] - : ['name' => $param]; + : ['name' => $param, 'optional' => $this->isParamOptional($route['uri'], $param)]; }); }); diff --git a/src/js/index.d.ts b/src/js/index.d.ts index 95c49457..78e2420a 100644 --- a/src/js/index.d.ts +++ b/src/js/index.d.ts @@ -61,12 +61,27 @@ type HasQueryParam = { _query?: Record }; */ type GenericRouteParamsObject = Record & HasQueryParam; // `keyof any` essentially makes it function as a plain `Record` + +/** + * Get only params for `optional: true` or `optional: false`. + */ +type GetParamsForOptionalSwitch = Extract< + I[number], + { optional: TOptional } +>; + +/** + * Map our parameter info to a routable object. We call twice to handle optional and non-optional params. + */ +type MapParamsToRoutable = + { [T in GetParamsForOptionalSwitch as T['name']]?: Routable } & + { [T in GetParamsForOptionalSwitch as T['name']]: Routable } + /** * An object of parameters for a specific named route. */ -// TODO: The keys here could be non-optional (or more detailed) if we can determine which params are required/not. -type KnownRouteParamsObject = { - [T in I[number] as T['name']]?: Routable; +type KnownRouteParamsObject> = { + [K in keyof MappedParams]: MappedParams[K] } & GenericRouteParamsObject; // `readonly` allows TypeScript to determine the actual values of all the // parameter names inside the array, instead of just seeing `string`. diff --git a/tests/Unit/CommandRouteGeneratorTest.php b/tests/Unit/CommandRouteGeneratorTest.php index 1cd3fded..ea1ff9b5 100644 --- a/tests/Unit/CommandRouteGeneratorTest.php +++ b/tests/Unit/CommandRouteGeneratorTest.php @@ -147,6 +147,7 @@ public function can_generate_dts_file() { app('router')->get('posts', $this->noop())->name('posts.index'); app('router')->post('posts/{post}/comments', PostCommentController::class)->name('postComments.store'); + app('router')->post('posts/{post}/comments{postId?}', PostCommentController::class)->name('postComments.store'); app('router')->getRoutes()->refreshNameLookups(); Artisan::call('ziggy:generate', ['--types' => true]); @@ -165,7 +166,7 @@ public function can_generate_dts_file() /** @test */ public function can_generate_dts_file_with_scoped_bindings() { - if (! $this->laravelVersion(7)) { + if (!$this->laravelVersion(7)) { $this->markTestSkipped('Requires Laravel >=7'); } @@ -275,7 +276,8 @@ public function __toString(): string class PostCommentController { - public function __invoke($post, $comment) { + public function __invoke($post, $comment) + { // } } diff --git a/tests/fixtures/ziggy-7.d.ts b/tests/fixtures/ziggy-7.d.ts index c71ee83b..44a29985 100644 --- a/tests/fixtures/ziggy-7.d.ts +++ b/tests/fixtures/ziggy-7.d.ts @@ -4,7 +4,8 @@ declare module 'ziggy-js' { "posts.index": [], "postComments.show": [ { - "name": "post" + "name": "post", + "optional": false }, { "name": "comment", @@ -13,7 +14,8 @@ declare module 'ziggy-js' { ], "postComments.store": [ { - "name": "post" + "name": "post", + "optional": false } ] } diff --git a/tests/fixtures/ziggy.d.ts b/tests/fixtures/ziggy.d.ts index 90ea8c89..7705443a 100644 --- a/tests/fixtures/ziggy.d.ts +++ b/tests/fixtures/ziggy.d.ts @@ -4,7 +4,12 @@ declare module 'ziggy-js' { "posts.index": [], "postComments.store": [ { - "name": "post" + "name": "post", + "optional": false + }, + { + "name": "postId", + "optional": true } ] }