fumeapp / modeltyper

Generate TypeScript interfaces from Laravel Models
MIT License
135 stars 16 forks source link

Support enums used as return type #48

Closed Jared0430 closed 1 year ago

Jared0430 commented 1 year ago

When using an Enum as a Cast via protected $casts = ['status' => App\Enums\Status::class];, the Enum gets exported as a TypeScript type

This however isn't the case when an Enum is used as a return type on an attribute mutator. This applies to both the classic getStatusAttribute style and the new Laravel 9 style. Currently, the Enum is referenced in the TypeScript output but will result in an error as the Enum type is not defined

This PR fixes that by attempting to reflect the return type and check if it is an Enum

I'm not very familiar with the codebase so there may be a better place to do this, if so, let me know and I'll see if I have the time to amend the PR

protected function status(): Attribute
{
    return Attribute::make(
        get: fn (): Status => Status::pending,
    );
}

// or...

public function getStatusAttribute(): Status
{
    return Status::pending;
}

will now export the relevant type

const Status = {
    Active: 'active',
    Pending: 'pending',
    Inactive: 'inactive',
} as const;

export type Status = (typeof Status)[keyof typeof Status];
tcampbPPU commented 1 year ago

hey @Jared0430 thanks for the PR, I will checkout & review later today, but in the meantime, are you able to write a test case for this? like in test/Tests/Feature/Actions/WriteColumnAttributeTest.php

tcampbPPU commented 1 year ago

hey @Jared0430 did some testing, for the most part everything is looking good, so thanks for that! However I did run into 1 thing when testing, per your example I do not get a consistent type that is written to the TS file when using either options of Laravel Attributes

I setup a small test, In test/laravel-skeleton/app/Models/User.php I added:

protected function roleEnum(): Attribute
{
    return Attribute::make(
        get: fn (): Roles => Roles::ADMIN,
    );
}

public function getRoleEnumTraditionalAttribute(): Roles
{
    return Roles::ADMIN;
}

Then when I run vendor/bin/phpunit --filter testCommandGeneratesExpectedOutputForUserModel test/Tests/Feature/Console/ModelTyperCommandTest.php

This is the output I get:

export interface User {
  // mutators
  role_traditional: string
  role_new: string
  role_enum: unknown
  role_enum_traditional: Roles
}

const Roles = {
  /** Can do anything */
  ADMIN: 'admin',
  /** Standard readonly */
  USER: 'user',
} as const;

export type Roles = typeof Roles[keyof typeof Roles]

The Enum part works great, but the issue is in the User interface.

role_enum: unknown
role_enum_traditional: Roles

Ideally these would both be the same values.

Jared0430 commented 1 year ago

I'll try to take a look at this when I can over the next week or so

Jared0430 commented 1 year ago

Thanks for finishing this off in my absence!

tcampbPPU commented 1 year ago

np, appreciate the contributions!