Skip to content

Should we allow to disable reflection on a specific type? #4241

Open

Description

@MangelMaxime

With the code below,

type SubType<'T> =
    {
        SubValue: 'T
    }

[<Erase>]
type TypeA<'T> = TypeA of (unit -> SubType<'T>)

type Updates =
    {
        Func : TypeA<unit>
    }

Fable generates this error:

error FABLE: Cannot get type info of generic parameter T. Fable erases generics at runtime, try inlining the functions so generics can be resolved at compile time.

This is because, here Func : TypeA<unit> has a concrete type giving.

However, for a library I am working on I kind of need this type of code supported.

A workaround, is to enable --noReflection flag in Fable but it means not reflection can happen anywhere, so perhaps it could be nice to introduce a [<NoReflection>] attribute to disable it only for specific types.

Another workaround is to use an anonymous record:

type Updates =
    {|
        VarView : TypeA<unit>
    |}

I will see if I can rewrite my library to use the anonymous record, as it would be better to not introduce an attribute for a really specific usage.

Activity

  1. MangelMaxime commented on Oct 23, 2025

    @MangelMaxime
    MemberAuthor

    Note, in my situation I Updates have additional properties who are mark as mutable which is invalid for anonymous records

  2. github-actions commented on Apr 5, 2026

    @github-actions
    Contributor

    🤖 This is an automated response from Repo Assist.

    Here's a technical analysis of the options for this request.

    Current behaviour

    Fable's reflection system emits type info for every type that might need it at runtime (used by typeof<T>, typedefof<T>, and serialisation libraries). When a generic erased-union type is stored in a record field with a concrete type parameter (like Func: TypeA<string>), reflection tries to resolve the generic and fails.

    Options

    Option 1: Per-type [<NoReflection>] attribute

    This would be a new attribute in Fable.Core that suppresses reflection emission for the decorated type. The main places to change:

    • src/Fable.Core/Fable.Core.fs — add [<AttributeUsage(AttributeTargets.Class ||| AttributeTargets.Struct)>] NoReflectionAttribute
    • src/Fable.Transforms/Transforms.Util.fs — in getEntityInfoHelper (or wherever type info is generated), check for the attribute and return a simpler/empty info
    • src/Fable.Transforms/Replacements.Util.fs — in typeInfo and related helpers, skip reflection for types with the attribute

    This is feasible but touches Fable.Core (distributed on NuGet) and shared transform code. It would need careful design: if reflection is disabled for TypeA<'T>, what happens when code calls typeof<TypeA<string>>? It should either return a dummy type or raise a compile-time warning.

    Option 2: Use --noReflection with selective opt-in

    The existing --noReflection flag disables reflection globally. A --noReflection global flag combined with a [<EnableReflection>] attribute to opt specific types back in would be the inverse approach. This might be simpler since the default path (no reflection) is already implemented.

    Option 3: Use a type wrapper without generics for the field

    As you noted, anonymous records work around this. Another pattern is to wrap in a non-generic interface:

    type ITypeA = interface end
    [<Erase>] type TypeA<'T> = TypeA of (unit -> SubType<'T>) interface ITypeA
    type Updates = { Func: ITypeA }

    This loses type safety but avoids the generic reflection issue.

    Recommendation

    Option 1 seems most user-friendly. The implementation should:

    1. Accept the attribute on type declarations
    2. When the type has [<NoReflection>], emit a placeholder type info (e.g., { fullname: "...", generics: () => [] }) or no type info at all
    3. Issue a compile-time warning if typeof<T> is used on such a type

    This is a good candidate for a focused PR in Fable.Core and Replacements.Util.fs. If you'd like to pursue it, the key entry point to look at is typeInfo in src/Fable.Transforms/Replacements.Util.fs and the attribute check pattern already used for [<Erase>] types.

    Note

    🔒 Integrity filter blocked 43 items

    The following items were blocked because they don't meet the GitHub integrity level.

    To allow these resources, lower min-integrity in your GitHub frontmatter:

    tools:
      github:
        min-integrity: approved  # merged | approved | unapproved | none

    Generated by Repo Assist · ● 5.1M · ◷

    To install this agentic workflow, run

    gh aw add githubnext/agentics/workflows/repo-assist.md@346204513ecfa08b81566450d7d599556807389f
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions