Skip to content

[<FSharp.Core.DefaultValue>] not handled properly on static fields #2739

Closed

Description

@chkn

Description

When a static field has the [<FSharp.Core.DefaultValue>] attribute, Fable leaves its value as undefined, which can have different behavior than the .NET default value.

Repro code

type Foo() =
    [<FSharp.Core.DefaultValue>]
    static val mutable private count : int
    static member IncrCount() =
        Foo.count <- Foo.count + 1
        Foo.count

printfn "%d" (Foo.IncrCount())

Expected and actual results

dotnet fsi prints: 1
Fable REPL prints: 0

Related information

  • Fable version: 3.7.0-beta-011
  • Operating system: macOS 10.15.7

Activity

  1. chkn commented on Jan 17, 2022

    @chkn
    ContributorAuthor

    I found a workaround: save the static field to a local first. This works as expected:

        static member IncrCount() =
            let count = Foo.count
            Foo.count <- count + 1
            Foo.count
  2. github-actions commented on Mar 17, 2026

    @github-actions
    Contributor

    🤖 This is an automated response from Repo Assist.

    Root cause: Fable doesn't emit static field initializers in the generated JavaScript class body. In declareClassWithParams in src/Fable.Transforms/Fable2Babel.fs (~line 3467), the classFields array only adds TypeScript type-annotation declarations for fields; no default-value initialization is emitted for either JavaScript or TypeScript:

    let classFields =
        if com.IsTypeScript && not ent.IsFSharpUnion then
            ent.FSharpFields
            |> List.mapToArray (fun field ->
                // Comment in the source: "Static fields need to be initialized by static constructor"
                ClassMember.classProperty (prop, isComputed = isComputed, isStatic = field.IsStatic, ...)
            )
        else
            Array.empty  // <-- nothing for JS

    So a field like static val mutable private count : int with [(DefaultValue)] is never emitted as static count = 0; in the JavaScript output. At runtime Foo.count is undefined. When you compute undefined + 1, JavaScript produces NaN, and since integer arithmetic is coerced via | 0, NaN | 0 = 0, causing the method to return 0 instead of 1.

    The fix would need to:

    1. Add a makeDefaultValue function for the JS target (the Python target already has getDefaultValueForType in Fable2Python.Util.fs as a reference)
    2. In declareClassWithParams, emit static count = 0; (or the appropriate default for the field's type) for static FSharpFields — either only when [(DefaultValue)] is present, or unconditionally for all static fields of value types

    Note: PR #4405 (currently open) fixes a related but distinct issue — static backing fields for static member val being incorrectly included as constructor parameters. This [(DefaultValue)] initialization issue is a separate bug that will still need a fix after #4405 is merged.

    Generated by Repo Assist · ◷

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f
    
  3. added a commit that references this issue on Mar 18, 2026
    28f34f0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions