Skip to content

[JS/TS] StringEnum now respect CompiledValue and CompiledName (by @shayanhabibi)#4144

Merged
MangelMaxime merged 6 commits intofable-compiler:mainfrom
shayanhabibi:compiledValueStringEnum
Mar 23, 2026
Merged

[JS/TS] StringEnum now respect CompiledValue and CompiledName (by @shayanhabibi)#4144
MangelMaxime merged 6 commits intofable-compiler:mainfrom
shayanhabibi:compiledValueStringEnum

Conversation

@shayanhabibi
Copy link
Copy Markdown
Contributor

@shayanhabibi shayanhabibi commented Jun 22, 2025

  • Adds optional boolean switch as second positional argument which toggles StringEnum respecting CompiledValue attributes.
  • Adds tests for StringEnum respecting CompiledValue
  • Adds remarks to xml docs to add this usage possibility

Example

Before:

[<StringEnum(CaseRules.LowerFirst)>]
type Behavior =
    | Replace
    | Combine
    | [<CompiledValue(false)>] None

console.log Behavior.None // 'none'
console.log Behavior.Replace // 'replace'

Now:

[<StringEnum(CaseRules.LowerFirst, true)>]
type Behavior =
    | Replace
    | Combine
    | [<CompiledValue(false)>] None

console.log Behavior.None // false
console.log Behavior.Replace // 'replace'

@shayanhabibi
Copy link
Copy Markdown
Contributor Author

How do I manage the error with the standalone test build failure?

@MangelMaxime
Copy link
Copy Markdown
Member

I not 100% sure but I think the Fable Standalone error is because we need to re-generate the metadata dll.

But before that what is the need behind this change?

I am not a fan of having a switch in StringEnum which then do something if we add another attribute somewhere else. It doesn't feels easy to understand to the user.

Potentially, we could decide that CompiledValue take precedence over StringEnum and so user would just need to add CompiledValue without having to pass a flag in StringEnum

@shayanhabibi
Copy link
Copy Markdown
Contributor Author

Only because it could be 'potentially' a change which impacts code bases. I, for instance, was not aware that compiled value didnt work with StringEnums, and only found when debugging some errors. But more importantly, I don't know how this would effect things like TypeScript generation.
Judging from your comment though, this is something you would be fine with?
Honestly my preference is to just have CompiledValue take precedence too.
Perhaps since we're doing a major version change, this is a better time than any to make the change. What do you think?

As for the reason behind this change, it is simply the fact that many enums in javascript land may consider true or false as independent special values, while at all other times they are strings.

@MangelMaxime
Copy link
Copy Markdown
Member

But more importantly, I don't know how this would effect things like TypeScript generation.

I don't use TypeScript generation personally so I am not sure, but from my perspective if the CI is green then it "probably" means we didn't break anything.

Honestly my preference is to just have CompiledValue take precedence too.

Can you please do it this way?

We also need to add tests for all the different values that CompiledValue supports and check that the output is supported by JavaScript/TypeScript.

As for the reason behind this change, it is simply the fact that many enums in javascript land may consider true or false as independent special values, while at all other times they are strings.

Not 100% related but this feels similar to glutinum-org/cli#111

And, from what I know I don't think we have a way in pure Fable/F# (without emit) to represent these types.

@shayanhabibi
Copy link
Copy Markdown
Contributor Author

Tried to check [<CompiledValue(null)>] against JS.undefined but got this:

image

so I removed that test.

Everything else works as expected.

@MangelMaxime
Copy link
Copy Markdown
Member

Regarding the failing CI, I will consider it normal and we are going to disable the test for Fable Standalone.

This is because on JavaScript we can't make the distinction between an int or a float:

match box 42 with
| :? int as value -> printfn "Int"
| :? float as value -> printfn "Int"
| _ -> printfn "Unknown"

compiles to

import { printf, toConsole } from "fable-library-js/String.js";

(function () {
    const matchValue = 42;
    if (typeof matchValue === "number") {
        const value = matchValue | 0;
        toConsole(printf("Int"));
    }
    else if (typeof matchValue === "number") {
        const value_1 = matchValue;
        toConsole(printf("Int"));
    }
    else {
        toConsole(printf("Unknown"));
    }
})();

There are ways to have a better distinction, but it will never be able to make a difference between 3 and 3.0 for example

shayanhabibi and others added 6 commits March 23, 2026 20:43
- Adds optional boolean switch as second positional argument which toggles StringEnum respecting CompiledValue attributes.
- Adds tests for StringEnum respecting CompiledValue
- Adds remarks to xml docs to add this usage possibility
…types.

CompiledName takes precedence over CompiledValue
@MangelMaxime MangelMaxime force-pushed the compiledValueStringEnum branch from 9b261b9 to 6535933 Compare March 23, 2026 19:50
@MangelMaxime MangelMaxime changed the title [Enhancement] StringEnum can opt-in to respect CompiledValueAttribute's on cases [JS/TS] StringEnum now respect CompiledValue and CompiledName (by @shayanhabibi) Mar 23, 2026
@MangelMaxime MangelMaxime merged commit c2a96bb into fable-compiler:main Mar 23, 2026
23 checks passed
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