attempting to set a non-property member in `jsOptions` generates in valid js
Description
When using jsOptions with an interface, a non-property member (one missing a getter or setting) generates invalid js, a local FSharpRef that references a non-existent identifier copyOfStruct.
Repro code
open Fable.Core.JsInterop
type Response =
abstract fn: int -> int
abstract fnProp: (int -> int) with get, set
abstract prop: bool with get, set
let res = jsOptions<Response> (
fun o ->
o.fn <- (fun i -> i)
o.fnProp <- (fun i -> i)
o.prop <- false
)
import { jsOptions } from "fable-library-js/Util.js";
import { FSharpRef } from "fable-library-js/Types.js";
export const res = jsOptions((o) => {
const addr = new FSharpRef(() => copyOfStruct, (v) => {
copyOfStruct = v;
});
addr.contents = ((i) => i);
o.fnProp = ((i_1) => i_1);
o.prop = false;
});
Related information
- Fable version: 4.18.0
- Operating system: OSX
I can't remember if Is abstract fn: int -> int equivalent to abstract fn: int -> int with get, set in F#?
I guess it is because the F# compiler don't complains during compilation.
For as long, as I remember we always recommended to use with get, set on interfaces used for POJO. However, I don't know if there is a reason for that or if supporting cases without with get, set has just been forgotten.
Going by the syntax tree alone, it seems to be a difference between the flags MemberKind:Member and MemberKind:PropertyGetSet:
https://sharplab.io/#v2:DYLgZgzgNALiCGEZQCYgNQB8YE8AOApgAQBKBEeA9gHYTEC8RRAsAFBPwBGSATvAMYwiYaiCIBLakIC0APglS2HbjD6Dh1AExiAFJJnz9ASiIB3cTAAWROjCVEuvAUJEAFHpTy79ROQpgm5lZEAOYEyDbhbEA===
Adding parens around the function type turns it from a plain member into a gettable property, which causes a compilation error in the example below since there's no setter defined. So it likes like a missing validation to me.
type Response =
abstract fn: int -> int
abstract fn2: (int -> int)
abstract fn3: (int -> int) with set
abstract fnProp: (int -> int) with get, set
abstract prop: bool with get, set
let res = jsOptions<Response> (
fun o ->
o.fn <- (fun i -> i)
// this line fails to compile
o.fn2 <- (fun i -> i)
o.fn3 <- (fun i -> i)
o.fnProp <- (fun i -> i)
o.prop <- false
)