Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TypeScript] POJO with [<ParamObject>] generates missing type hint. #4003

Open
Freymaurer opened this issue Jan 7, 2025 · 4 comments · May be fixed by #4007
Open

[TypeScript] POJO with [<ParamObject>] generates missing type hint. #4003

Freymaurer opened this issue Jan 7, 2025 · 4 comments · May be fixed by #4007

Comments

@Freymaurer
Copy link

Description

I am working on Fable transpiled React components for usage from typescript. I wanted to create the best possible native feeling for typescript users. My component uses a type which i had defined as record type before:

type Term = {
    Name: string option
    Id: string option
    Description: string option
    Href: string option
    IsObsolete: bool option
    Source: string option
    Data: obj option
} with
    static member init(?name, ?id, ?description, ?source: string, ?href, ?obsolete: bool, ?data) = {
        Name = name
        Id = id
        Source = source
        IsObsolete = obsolete
        Href = href
        Description = description
        Data = data
    }

To improve usability from ts I changed it according to the docs to a js pojo using [<ParamObject>] here.

[<AllowNullLiteral>]
[<Global>]
type Term
    [<ParamObjectAttribute; Emit("$0")>]
    (?name, ?id, ?description, ?source: string, ?href, ?isObsolete: bool, ?data) =
    member val name: string option = jsNative with get, set
    member val id: string option = jsNative with get, set
    member val description: string option = jsNative with get, set
    member val source: string option = jsNative with get, set
    member val href: string option = jsNative with get, set
    member val isObsolete: bool option = jsNative with get, set
    member val data: obj option = jsNative with get, set

Which works as intented but produces a type hint without reference:

image

Repro code

Here is the code with an example:

repl

Expected and actual results

I would expect fable to do one of the following transpilations with decreasing favorability:

  1. Transpile type to typescript interface (best solution)
  2. Transpile with pojo type hint (as such: { data?: Option<any>, description?: string, href?: string, id?: string, isObsolete?: boolean, name?: string, source?: string })
  3. transpile using any

Instead it produces a type hint towards the non-generated class, which can lead to huge issues, as having types with similiar names can lead to shadowing at worst.

Related information

  • Fable version: 4.19.0 and repl version 4.23.0
  • Operating system: Windows 11
@joprice
Copy link
Contributor

joprice commented Jan 7, 2025

Adding Global tells fable that the type is pre-existing, so no code will be gererated for it. Removing that attribute will produce a record class definition, but still not sure how usable that is. You might want AttachMembers to get non-mangled property names.

@Freymaurer
Copy link
Author

Hey! I know what the attributes do, the issue is more on the documentation and intent side.

The fable documentation says to simulate nice POJOs using the construct shown above with type Global, AllowNullLiteral and a constructor with ParamObject and Emit("$0"), while maintaining f# native typing. So i assume we also want the same kind of typing you would expect for typescript and pojos, which can be done with interfaces for example. 🙂

@joprice
Copy link
Contributor

joprice commented Jan 7, 2025

Ah I see. Yea in that case it seems to me to be a bug if it's still generating code that references the erased type for an effectively anonymous structurally typed pojo.

@MangelMaxime
Copy link
Member

We should try to generate an interface when the following rules applies

  1. Target is TypeScript
  2. Type is decorated with [<Global>]
  3. The constructor is decorated with [<ParamObject; Emit("$0")>]

This is IHMO what reflect the best the intention when we are applying theses attributes to a type. A POJO is equivalent to an interface in TypeScript.

For reference, when ParamObject was introduced I asked Alfonso if we could support ParamObject at the class level directly instead of doing all the ceremony we currently do for POJO but the answer was that it would make Fable more complex and introduce more magic. Discussion can be found here

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 a pull request may close this issue.

3 participants