Skip to content

Proposal: "plain-enums" option to output TypeScript enums usable at runtime - #128

Merged
tcampbPPU merged 6 commits into
fumeapp:masterfrom
lorenzodalaqua:feat/plain-enums-option
Aug 1, 2026
Merged

Proposal: "plain-enums" option to output TypeScript enums usable at runtime#128
tcampbPPU merged 6 commits into
fumeapp:masterfrom
lorenzodalaqua:feat/plain-enums-option

Conversation

@lorenzodalaqua

Copy link
Copy Markdown
Contributor

First off, thanks again for building and maintaining this package. I've been using it extensively, and it's worked great for keeping my TypeScript types in sync with my Eloquent models.

This PR is a proposal for a small addition that would help me integrate ModelTyper's output more fully into my TypeScript code, and I'd love feedback on whether it fits the direction you want for the API.

Problem

--use-enums currently always emits const enum. const enum members are inlined by the TypeScript compiler, so they don't produce a real object at runtime, they only exist as a compile-time construct. That means I can't do things like Object.values(Roles) on the generated enums in code that uses ModelTyper's definitions.

Proposal

I propose adding a --plain-enums flag (and matching plain-enums config key) that, when used together with --use-enums, emits a plain export enum instead of export const enum. A plain enum compiles to a real object, so its values are readable at runtime. Defaults to false, so nothing changes unless it's explicitly opted into.

Design choices

Please let me know if you disagree with any part of the approach, and I can adapt it to your vision for the package.

First, I used a new boolean flag, trying to match the existing options in the project. I considered making use-enums a three-way value (something like object/const/plain) instead, which would avoid the one meaningless combination (use-enums off, plain-enums on). I went with a new boolean flag instead because use-enums is an existing flag used by consumers, so I'd rather not create something backward-incompatible. Also, most other options in this package are boolean and read the same way, so I tried to keep that consistent. Changing use-enums's type felt like a bigger, riskier change than this PR needed to make, but I'm open to going the other direction if you'd rather have one option.

Second, I chose to default to false (opt-in) rather than make it a const-enums flag defaulting to true. Every boolean flag in this package is a presence-only (--flag) CLI option (no negative --no-flag option). A flag defaulting to true could not be turned off from the CLI without deviating from the other flags. Making the new behavior opt-in keeps it usable from the CLI alone, without needing to edit the config file, and being consistent with other flags.

Changes

  • src/Actions/WriteEnumConst.php: emits export enum instead of export const enum when plainEnums is set
  • src/Actions/Generator.php, GenerateCliOutput.php, GenerateJsonOutput.php: drill the new option through, alongside useEnums
  • src/Commands/ModelTyperCommand.php, config/modeltyper.php: new --plain-enums flag and config key
  • Tests covering both the unit level (WriteEnumConst, for both const enum and plain enum) and the full command
  • readme.md: documents the new flag

I am happy to adjust naming, defaults, or the scope based on your feedback.

Related

#54 asks for something similar but in the default (non --use-enums) object-literal path: exporting the const X = {...} as const object itself so it can be used directly at runtime, not just its derived type. This PR doesn't touch that path, it's specifically about the const enum issue on the --use-enums side, but I wanted to flag it as related since the underlying motivation (using generated enum-like values at runtime) is the same.

const enum members are inlined by the TypeScript compiler and produce
no runtime object, so consumers can't read enum values at runtime
(e.g. Object.values()). Add a plainEnums parameter, defaulting to
false, so the writer can emit a plain export enum instead when
runtime access is needed.
WriteEnumConst's new plainEnums parameter needs a path from the
command entrypoint down to where enums are written. Wire it through
Generator, GenerateCliOutput, and GenerateJsonOutput the same way
useEnums already reaches both output paths.
Exposes the plainEnums behavior through the same config-or-CLI
pattern as every other option.

I considered making use-enums itself a three-way value (object,
const, plain) instead of adding a second boolean, since that would
make the one meaningless combination (use-enums off, plain-enums on)
unrepresentable. I kept use-enums as a plain boolean and added a
modifier instead, because it's an existing public flag and every
other option in this package is a flat boolean; changing its type
would mean true/false permanently carry legacy meaning while new
string values mean something else.

I defaulted plain-enums to false rather than adding a const-enums
flag defaulting to true, because every boolean flag here is a
presence-only Artisan option with no --no-flag negation. A
default-true flag could be turned on redundantly from the CLI but
never off, forcing anyone who wants plain enums to edit the published
config file instead of just passing a flag.
Adds direct coverage for both branches of the useEnums=true path,
alongside a new test for plainEnums. The const enum branch was
previously only exercised indirectly through the full command, so
this pins its output at the unit level too.
Confirms the flag reaches generated output through the full command,
mirroring the existing use-enums command-level test.
Adds it to the Additional Options list and extends the existing
const enum note to explain why it isn't readable at runtime and how
--plain-enums opts out of that.
@tcampbPPU

Copy link
Copy Markdown
Member

this is great! i'm wondering if that should just be the default behavior? but i think for now id rather not introduce that for a minor version change. so how this is proposed now with the opt in seems like a good path to me

@tcampbPPU
tcampbPPU merged commit f06dc8d into fumeapp:master Aug 1, 2026
26 checks passed
@lorenzodalaqua

Copy link
Copy Markdown
Contributor Author

this is great! i'm wondering if that should just be the default behavior? but i think for now id rather not introduce that for a minor version change. so how this is proposed now with the opt in seems like a good path to me

Awesome! And I think making that the default behavior would make sense too, I just went with this path to avoid any breaking changes to existing configs 😅

Maybe you could invert the option to be called const-enums and default to false on a major version change 🤔

Anyway, thanks for merging!

@lorenzodalaqua
lorenzodalaqua deleted the feat/plain-enums-option branch August 3, 2026 12:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants