From 914ab376f1b41097811e3ce6080ab3dfc03eb8a5 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=A1=D0=BB=D0=B0=D0=B2=D0=B0=20=D0=A3=D0=BA=D1=80=D0=B0?=
=?UTF-8?q?=D1=97=D0=BD=D1=96!=20=D0=93=D0=B5=D1=80=D0=BE=D1=8F=D0=BC=20?=
=?UTF-8?q?=D1=81=D0=BB=D0=B0=D0=B2=D0=B0!?=
<777696+ncave@users.noreply.github.com>
Date: Sat, 15 Jun 2024 12:11:47 -0700
Subject: [PATCH] Fixes #3847 (#3852)
---
src/Fable.Cli/CHANGELOG.md | 4 +
src/Fable.Transforms/Fable2Babel.fs | 8 +-
.../Rust/AST/Rust.AST.Adapters.fs | 2 +-
src/Fable.Transforms/Rust/Fable2Rust.fs | 19 +-
src/Fable.Transforms/Transforms.Util.fs | 8 +-
tests/Dart/main.dart | 2 +
tests/Dart/src/Fable.Tests.Dart.fsproj | 1 +
tests/Dart/src/RecordTests.fs | 22 +
tests/Dart/src/TypeTests.fs | 1287 +++++++++++++++++
tests/Js/Main/OptionTests.fs | 161 ---
tests/Js/Main/RecordTypeTests.fs | 22 +
tests/Js/Main/TypeTests.fs | 154 ++
tests/Python/TestMisc.fs | 16 -
tests/Python/TestRecordType.fs | 24 +
tests/Python/TestType.fs | 162 +++
tests/Rust/tests/src/MiscTests.fs | 20 -
tests/Rust/tests/src/RecordTests.fs | 36 +-
tests/Rust/tests/src/TypeTests.fs | 162 +++
18 files changed, 1895 insertions(+), 215 deletions(-)
create mode 100644 tests/Dart/src/TypeTests.fs
diff --git a/src/Fable.Cli/CHANGELOG.md b/src/Fable.Cli/CHANGELOG.md
index d6a75715fa..2972555ce4 100644
--- a/src/Fable.Cli/CHANGELOG.md
+++ b/src/Fable.Cli/CHANGELOG.md
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
+### Fixed
+
+* [JS/TS] Fixed unwrapping optional arguments (#3847) (by @ncave)
+
## 4.19.2 - 2024-06-13
### Fixed
diff --git a/src/Fable.Transforms/Fable2Babel.fs b/src/Fable.Transforms/Fable2Babel.fs
index a2766ff1ef..a3db44c3c4 100644
--- a/src/Fable.Transforms/Fable2Babel.fs
+++ b/src/Fable.Transforms/Fable2Babel.fs
@@ -1891,10 +1891,10 @@ module Util =
let args =
match paramsInfo with
- | Some i when List.sameLength args i.Parameters ->
- List.zip args i.Parameters
- |> List.map (fun (a, i) ->
- if i.IsOptional then
+ | Some info when args.Length <= info.Parameters.Length ->
+ List.zipSafe args info.Parameters
+ |> List.map (fun (a, p) ->
+ if p.IsOptional then
unwrapOptionalArg com a |> snd
else
a
diff --git a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs
index b8b39ed66c..7940f9bce8 100644
--- a/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs
+++ b/src/Fable.Transforms/Rust/AST/Rust.AST.Adapters.fs
@@ -221,7 +221,7 @@ module fmt =
type Macros =
static member assert_eq(actual, expected) = assert (actual = expected)
static member assert_ne(actual, expected) = assert (actual <> expected)
- static member unreachable() = failwith "should not happen"
+ static member unreachable() = failwith "unreachable!"
static member panic() = failwith "panic!"
static member panic(str: string) = failwith str
diff --git a/src/Fable.Transforms/Rust/Fable2Rust.fs b/src/Fable.Transforms/Rust/Fable2Rust.fs
index f4166aa7d7..1b355e02e2 100644
--- a/src/Fable.Transforms/Rust/Fable2Rust.fs
+++ b/src/Fable.Transforms/Rust/Fable2Rust.fs
@@ -1615,6 +1615,21 @@ module Util =
// let rest = List.rev rest |> List.map (fun e -> com.TransformExpr(ctx, e))
// rest @ [Expression.spreadElement(com.TransformExpr(ctx, last))]
| args ->
+ let optionalArgs =
+ if args.Length < parameters.Length then
+ parameters
+ |> List.skip args.Length
+ |> List.filter (fun p -> p.IsOptional)
+ |> List.map (fun p ->
+ match p.Type with
+ | Fable.Option(t, isStruct) -> Fable.Value(Fable.NewOption(None, t, isStruct), None)
+ | _ -> failwith "unreachable"
+ )
+ else
+ []
+
+ let args = List.append args optionalArgs
+
let argsWithTypes =
if argTypes.Length = args.Length then
args |> List.zip argTypes |> List.map (fun (t, a) -> Some t, a)
@@ -2877,7 +2892,7 @@ module Util =
|> prepareRefForPatternMatch com ctx fableExpr.Type (tryGetIdentName fableExpr)
mkLetExpr pat expr
- | _ -> failwith "Should not happen"
+ | _ -> failwith "unreachable"
let transformTest (com: IRustCompiler) ctx range kind (fableExpr: Fable.Expr) : Rust.Expr =
match kind with
@@ -2923,7 +2938,7 @@ module Util =
let thenExpr = mkBoolLitExpr true
let elseExpr = mkBoolLitExpr false
mkIfThenElseExpr guardExpr thenExpr elseExpr
- | _ -> failwith "Should not happen"
+ | _ -> failwith "unreachable"
let transformSwitch (com: IRustCompiler) ctx (evalExpr: Fable.Expr) cases defaultCase targets : Rust.Expr =
let namesForIndex evalType evalName caseIndex = //todo refactor with below
diff --git a/src/Fable.Transforms/Transforms.Util.fs b/src/Fable.Transforms/Transforms.Util.fs
index 3d2bcf8b8e..ce53caff10 100644
--- a/src/Fable.Transforms/Transforms.Util.fs
+++ b/src/Fable.Transforms/Transforms.Util.fs
@@ -1265,11 +1265,9 @@ module AST =
| None -> args, []
| Some index when index > args.Length || index > info.Parameters.Length -> args, []
| Some index ->
- let args, namedValues = List.splitAt index args
-
- let namedKeys = List.skip index info.Parameters |> List.truncate namedValues.Length
-
- args, List.zipSafe namedKeys namedValues
+ let args, namedArgs = List.splitAt index args
+ let namedParams = List.skip index info.Parameters |> List.truncate namedArgs.Length
+ args, List.zipSafe namedParams namedArgs
/// Used to compare arg idents of a lambda wrapping a function call
let argEquals (argIdents: Ident list) argExprs =
diff --git a/tests/Dart/main.dart b/tests/Dart/main.dart
index 918dcc1607..15ad902719 100644
--- a/tests/Dart/main.dart
+++ b/tests/Dart/main.dart
@@ -23,6 +23,7 @@ import './src/SudokuTests.dart' as sudoku;
import './src/TailCallTests.dart' as tailcall;
import './src/TimeSpanTests.dart' as timespan;
import './src/TupleTests.dart' as tuple;
+import './src/TypeTests.dart' as type_;
import './src/UnionTests.dart' as union;
void main() {
@@ -51,5 +52,6 @@ void main() {
tailcall.tests();
timespan.tests();
tuple.tests();
+ type_.tests();
union.tests();
}
\ No newline at end of file
diff --git a/tests/Dart/src/Fable.Tests.Dart.fsproj b/tests/Dart/src/Fable.Tests.Dart.fsproj
index 8b981984a0..bcfa914a07 100644
--- a/tests/Dart/src/Fable.Tests.Dart.fsproj
+++ b/tests/Dart/src/Fable.Tests.Dart.fsproj
@@ -33,6 +33,7 @@
+
diff --git a/tests/Dart/src/RecordTests.fs b/tests/Dart/src/RecordTests.fs
index 9c2a2622c7..179801b326 100644
--- a/tests/Dart/src/RecordTests.fs
+++ b/tests/Dart/src/RecordTests.fs
@@ -53,6 +53,28 @@ let tests() =
// x = {| y with Baz = 23 |} |> equal true // Doesn't compile
x = {| y with Bar = 14 |} |> equal false
+ testCase "Anonymous records can have optional fields" <| fun () ->
+ let add (o: {| bar: int option; zas: string option; foo: int option option |}) =
+ let bar = o.bar |> Option.map string |> Option.defaultValue "-"
+ let zas = defaultArg o.zas ""
+ let foo = match o.foo with Some(Some i) -> string i | Some None -> "xx" | None -> "x"
+ bar + zas + foo
+
+ {| bar = Some 3; zas = Some "ooooo"; foo = Some None |} |> add |> equal "3oooooxx"
+ {| bar = Some 22; zas = Some ""; foo = Some(Some 999) |} |> add |> equal "22999"
+ {| bar = None; zas = None; foo = None |} |> add |> equal "-x"
+ {| foo = Some None; bar = None; zas = None |} |> add |> equal "-xx"
+
+ testCase "Anonymous records can have optional function fields" <| fun () ->
+ let add (o: {| bar: (int -> int -> int) option; foo: int -> int -> int |}) =
+ let fn = o.bar
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ o.foo 3 4 + f1 + f2
+
+ {| bar = Some (+); foo = (*) |} |> add |> equal 36
+ {| bar = None; foo = (+) |} |> add |> equal -1
+
testCase "SRTP works with anonymous records" <| fun () ->
let ar = [| {|Id=Id"foo"; Name="Sarah"|}; {|Id=Id"bar"; Name="James"|} |]
replaceById {|Id=Id"ja"; Name="Voll"|} ar |> Seq.head |> fun x -> equal "Sarah" x.Name
diff --git a/tests/Dart/src/TypeTests.fs b/tests/Dart/src/TypeTests.fs
new file mode 100644
index 0000000000..5908b9a442
--- /dev/null
+++ b/tests/Dart/src/TypeTests.fs
@@ -0,0 +1,1287 @@
+module Fable.Tests.Dart.Type
+
+open System
+// open System.Runtime.InteropServices
+open Fable.Core
+open Util
+
+// // Check if custom attributes can be created
+// type MyAttribute() =
+// inherit System.Attribute()
+
+// type ITest = interface end
+// type ITest2 = interface end
+
+// type ITest3 =
+// abstract Add2: int * int -> int
+
+// type ITest4 =
+// inherit ITest3
+// abstract Add: int * int -> int
+
+// type TestType(s: string) =
+// member _.Value = s
+// interface ITest
+
+// type TestType2(s: string) =
+// member _.Value = s
+// interface ITest
+
+// type TestType3() =
+// member _.Value = "Hi"
+// interface ITest
+
+// type TestType4() =
+// inherit TestType3()
+// member _.Value2 = "Bye"
+// interface ITest2
+
+// type TestType5(greeting: string) =
+// member _.Value = greeting
+// member _.Overload(x) = x + x
+// member _.Overload(x, y) = x + y
+
+// type TestType8(?greeting) =
+// member _.Value = defaultArg greeting "Hi"
+
+// type TestType9() =
+// inherit TestType8()
+// let foo = TestType8("Hello")
+// member _.Greet(name) = foo.Value + " " + name
+
+// type TestType10Base() =
+// interface ITest4 with
+// member _.Add2(x, y) = x - y
+// member _.Add(x, y) = x + y
+
+// type TestType10Child() =
+// inherit TestType10Base()
+
+// type RenderState =
+// { Now : int
+// Players : Map
+// Map : string }
+
+// type T4 = TestType4
+
+// type TestType6(x: int) =
+// let mutable i = x
+// member val Value1 = i with get, set
+// member _.Value2 = i + i
+// member _.Value3 with get() = i * i and set(v) = i <- v
+
+// type TestType7(a1, a2, a3) =
+// let arr = [|a1; a2; a3|]
+// member _.Value with get(i) = arr.[i] and set(i) (v) = arr.[i] <- v
+
+// []
+// type TestTypeAttached(a1, a2, a3) =
+// let arr = [| a1; a2; a3 |]
+// member _.Value1
+// with get () = arr.[1]
+// and set (v) = arr.[1] <- v
+// member _.Value
+// with get (i) = arr.[i]
+// and set (i) (v) = arr.[i] <- v
+// member _.Item
+// with get (i) = arr.[i]
+// and set (i) (v) = arr.[i] <- v
+
+// type ITestProps =
+// abstract Value1: float with get, set
+// abstract Value: int -> float with get, set
+// abstract Item: int -> float with get, set
+
+// type TestProps(arr: float[]) =
+// interface ITestProps with
+// member _.Value1
+// with get () = arr.[1]
+// and set (v) = arr.[1] <- v
+// member _.Value
+// with get (i) = arr.[i]
+// and set (i) (v) = arr.[i] <- v
+// member _.Item
+// with get (i) = arr.[i]
+// and set (i) (v) = arr.[i] <- v
+
+// type A = { thing: int } with
+// member x.show() = string x.thing
+// static member show (x: A) = "Static: " + (string x.thing)
+
+// type B = { label: string } with
+// member x.show() = x.label
+// static member show (x: B) = "Static: " + x.label
+
+// let inline show< ^T when ^T : (member show : unit -> string)> (x:^T) : string =
+// (^T : (member show : unit -> string) (x))
+
+// let inline showStatic< ^T when ^T : (static member show : ^T -> string)> (x:^T) : string =
+// (^T : (static member show : ^T -> string) (x))
+
+// []
+// type Serializable(?i: int) =
+// let mutable deserialized = false
+// let mutable publicValue = 1
+// let mutable privateValue = defaultArg i 0
+// member x.PublicValue
+// with get() = publicValue
+// and set(i) = deserialized <- true; publicValue <- i
+// override x.ToString() =
+// sprintf "Public: %i - Private: %i - Deserialized: %b"
+// publicValue privateValue deserialized
+
+// type SecondaryCons(x: int) =
+// new () = SecondaryCons(5)
+// member _.Value = x
+
+// // type SecondaryConsChild() =
+// // inherit SecondaryCons()
+
+// type MultipleCons(x: int, y: int) =
+// new () = MultipleCons(2,3)
+// new (x:int) = MultipleCons(x,4)
+// member _.Value = x + y
+
+// []
+// type AbstractClassWithDefaults () =
+// abstract MethodWithDefault : unit -> string
+// default x.MethodWithDefault () = "Hello "
+
+// abstract MustImplement: unit -> string
+
+// member x.CallMethodWithDefault () =
+// x.MethodWithDefault() + x.MustImplement()
+
+// type ConcreteClass () =
+// inherit AbstractClassWithDefaults()
+// override x.MustImplement () = "World!!"
+
+// type ConcreteClass2 () =
+// inherit AbstractClassWithDefaults()
+// override x.MethodWithDefault () = "Hi "
+// override x.MustImplement () = "World!!"
+
+// []
+// type AbstractClass3() =
+// abstract MyProp: int with get, set
+
+// type ConcreteClass3() =
+// inherit AbstractClass3()
+// let mutable v = 5
+// override __.MyProp with get() = v and set(v2) = v <- v + v2
+
+// type ISomeInterface =
+// abstract OnlyGetProp: int with get
+// abstract OnlyProp: int
+// abstract Sender : int with get, set
+
+// type XISomeInterface () =
+// let mutable i = 0
+// interface ISomeInterface with
+// member x.OnlyGetProp
+// with get () = 0
+// member x.OnlyProp = 3
+// member x.Sender
+// with get () = i
+// and set i' = i <- i'
+
+// type IFoo =
+// abstract Foo: unit -> string
+// abstract Bar: string
+// abstract MySetter: int with get, set
+
+// let mangleFoo(x: IFoo) = x.Foo()
+
+// type FooImplementor(i: int) =
+// let mutable mut1 = 0
+// let mutable mut2 = 5
+// new () = FooImplementor(1)
+
+// member x.Foo() = String.replicate i "foo"
+// member x.Bar = "he"
+// member x.MySetter with get() = mut1 and set(v) = mut1 <- v + 2
+
+// interface IFoo with
+// member x.Foo() = x.Foo() + "bar"
+// member x.Bar = x.Bar + "ho"
+// member x.MySetter with get() = mut1 + mut2 and set(v) = mut2 <- v + 3
+
+// type FooImplementorChild() =
+// inherit FooImplementor(3)
+
+// []
+// type AbstractFoo() =
+// abstract member Foo2: unit -> string
+// interface IFoo with
+// member this.Foo() = this.Foo2() + "FOO"
+// member x.Bar = ""
+// member x.MySetter with get() = 0 and set(v) = ()
+
+// type ChildFoo() =
+// inherit AbstractFoo()
+// override this.Foo2() = "BAR"
+
+// type BaseClass (x: int) =
+// abstract member Init: unit -> int
+// default __.Init () = x
+// abstract member Prop: string
+// default __.Prop = "base"
+
+// type ExtendedClass () =
+// inherit BaseClass(5)
+// override __.Init() = base.Init() + 2
+// override __.Prop = base.Prop + "-extension"
+
+// type BaseClass2() =
+// let field = 1
+// member _.A() = field
+
+// type ExtendedClass2() =
+// inherit BaseClass2()
+// member _.A() = 2
+// member _.B() = base.A()
+
+// type Employee = { name: string; age: float; location: Location }
+// and Location = { name: string; mutable employees: Employee list }
+
+// []
+// type ValueType<'T> =
+// new (v) = { value = v }
+// val value : 'T
+// member x.Value = x.value
+
+// []
+// type ValueType1<'T>(value: 'T) =
+// member x.Value = value
+
+// []
+// type ValueType2(i: int, j: int) =
+// member x.Value = i + j
+
+// []
+// type ValueType3 =
+// struct
+// val mutable public X : int
+// end
+
+// []
+// type StructUnion = Value of string
+
+// []
+// type SimpleRecord = { A: string; B: string }
+
+// type Point2D =
+// struct
+// val X: float
+// val Y: float
+// new(xy: float) = { X = xy; Y = xy }
+// end
+
+// exception MyEx of int*string
+
+// type MyEx2(f: float) =
+// inherit exn(sprintf "Code: %i" (int f))
+// member _.Code = f
+
+// type ThisContextInConstructor(v) =
+// let f () = v
+// member val Value = f
+
+// type DowncastTest(value: int) =
+// member _.Value = value
+// interface System.IDisposable with
+// member _.Dispose() = ()
+
+// []
+// type TypeWithClassAttribute =
+// val Pos : int
+// new (pos) = { Pos=pos }
+
+// // -------------------------------------------------------------
+// // Issue #1975: https://github.com/fable-compiler/Fable/issues/1975
+// // In previous version of Fable, using type with parameterized units of measure was causing an endless loops in the compiler
+
+// type TestTypeWithParameterizedUnitMeasure<[] 't> =
+// private | TestTypeWithParameterizedUnitMeasureType of float<'t>
+
+// member this.Value =
+// match this with
+// | TestTypeWithParameterizedUnitMeasureType value -> value
+
+// let makeTestTypeWithParameterizedUnitMeasureType (value: float<_>) : TestTypeWithParameterizedUnitMeasure<_> =
+// TestTypeWithParameterizedUnitMeasureType value
+
+// open FSharp.Data.UnitSystems.SI.UnitSymbols
+
+// type Test_TestTypeWithParameterizedUnitMeasure = {
+// Field: TestTypeWithParameterizedUnitMeasure
+// }
+
+// // -------------------------------------------------------------
+
+// // Test ported from https://github.com/fable-compiler/Fable/pull/1336/files
+// // type TestTypeWithDefaultValue() =
+// // [] val mutable IntValue: int
+// // [] val mutable StringValue: string
+// // [] val mutable ObjValue: System.Collections.Generic.Dictionary
+
+// type Default1 = int
+
+// type Distinct1 =
+// // Overloads only distinguished by generic constrain work, see #1908
+// static member inline Distinct1 (x: ^``Collection<'T>``, _impl: Default1) = (^``Collection<'T>`` : (static member Distinct1 : _->_) x) : '``Collection<'T>``
+// static member inline Distinct1 (_: ^t when ^t : null and ^t : struct, _mthd: Default1) = id //must
+
+// // Overloads only distinguished by struct tuple work, see #2417
+// static member OfList(_elements: list<'K * 'V>) = ()
+// static member OfList(_elements: list) = ()
+
+// type InfoA = {
+// Foo: string
+// }
+
+// type InfoB = {
+// InfoA: InfoA
+// Bar: string
+// }
+
+// []
+// type InfoAClass(info: InfoA) =
+// abstract WithInfo: InfoA -> InfoAClass
+// member _.Foo = info.Foo
+// member this.WithFoo foo =
+// this.WithInfo({ info with Foo = foo })
+
+// type InfoBClass(info: InfoB) =
+// inherit InfoAClass(info.InfoA)
+// override this.WithInfo(infoA) =
+// InfoBClass({ info with InfoA = infoA }) :> InfoAClass
+
+// type FooInterface =
+// abstract Foo: string with get, set
+// abstract DoSomething: f: (float -> float -> float) * v: float -> float
+// abstract Item: int -> char with get, set
+// abstract Sum: [] items: string[] -> string
+
+// []
+// type BarInterface =
+// abstract Bar: string with get, set
+// abstract DoSomething: f: (float -> float -> float) * v: float -> float
+// abstract Item: int -> char with get, set
+// abstract Item: char -> bool with get
+// abstract Sum: [] items: string[] -> string
+
+// []
+// type FooAbstractClass(x: float) =
+// member _.Value = x
+// member _.DoSomething(x, y) = x * y
+// abstract DoSomething: float -> float
+
+// type FooClass(x) =
+// inherit FooAbstractClass(5.)
+// let mutable x = x
+// override this.DoSomething(x) =
+// this.DoSomething(x, this.Value)
+// static member ChangeChar(s: string, i: int, c: char) =
+// s.ToCharArray() |> Array.mapi (fun i2 c2 -> if i = i2 then c else c2) |> String
+// interface FooInterface with
+// member _.Foo with get() = x and set(y) = x <- y
+// member this.DoSomething(f, x) =
+// let f = f x
+// let x = f 2.
+// let y = f 8.
+// this.DoSomething(x + y)
+// member _.Item with get(i) = x.[i] and set i c = x <- FooClass.ChangeChar(x, i, c)
+// member _.Sum(items) = Array.reduce (fun x y -> x + y + x + y) items
+
+// []
+// type BarAbstractClass(x: float) =
+// member _.Value = x
+// member _.DoSomething(x, y) = x ** y
+// abstract DoSomething: float -> float
+
+// type BarClass(x) =
+// inherit BarAbstractClass(10.)
+// let mutable x = x
+// override this.DoSomething(x) =
+// this.DoSomething(x, this.Value)
+// interface BarInterface with
+// member _.Bar with get() = x and set(y) = x <- y
+// member this.DoSomething(f, x) =
+// let f = f x
+// let x = f 4.5
+// let y = f 7.
+// this.DoSomething(x - y)
+// member _.Item with get(i) = x.[i] and set i c = x <- FooClass.ChangeChar(x, i + 1, c)
+// member _.Item with get(c) = x.ToCharArray() |> Array.exists ((=) c)
+// member _.Sum(items) = Array.reduce (fun x y -> x + x + y + y) items
+
+// type Interface2 =
+// abstract Value: int
+// abstract Add: unit -> int
+
+// type Interface1 =
+// abstract Create: int -> Interface2
+
+// type MixedThese(x: int) =
+// member _.Value = x
+// interface Interface1 with
+// member this1.Create(y: int) =
+// { new Interface2 with
+// member _.Value = y
+// member this2.Add() = this1.Value + this2.Value }
+
+// let areEqual (x: obj) (y: obj) =
+// x = y
+
+// type MyUnion1 = Foo of int * int | Bar of float | Baz
+// type MyUnion2 = Foo of int * int
+// with override _.ToString() = "ffff"
+
+// type MyRecord1 = { Foo: int; Bar: string }
+// type MyRecord2 = { Foo: int; Bar: string }
+
+// type SubclassTest1() = class end
+// type SubclassTest2() = inherit SubclassTest1()
+// type SubclassTest3() = inherit SubclassTest2()
+
+// [] type a
+// [] type b
+// [] type c = a * b
+// [] type d = a / b
+
+// type MeasureTest<[] 'T> = { X: float<'T> }
+// type MeasureTestGen<[] 'T, 'V> = { X: float<'T>; Y: 'V }
+
+// type MeasureTest1_ = { Y: MeasureTestGen }
+// type MeasureTest1 = { Y: MeasureTest }
+// type MeasureTest2 = { Y: float }
+// type MeasureTest3 = { Y: MeasureTest }
+// type MeasureTest4 = { Y: float }
+
+// // Check that types with product measures compile, see #2532
+// type MeasureTest5 = { Y: MeasureTest }
+// type MeasureTest6 = { Y: MeasureTest }
+
+// type EnumFoo =
+// | Foo = 0
+// | Bar = 1
+
+// []
+// type MangledAbstractClass1() =
+// class end
+
+// []
+// type MangledAbstractClass2(v: int) =
+// inherit MangledAbstractClass1()
+// abstract MyMethod: int -> int
+// default _.MyMethod(x: int) = x * v
+
+// []
+// type MangledAbstractClass3(v) =
+// inherit MangledAbstractClass2(v + 3)
+
+// []
+// type MangledAbstractClass4(v) =
+// inherit MangledAbstractClass3(v + 4)
+// override _.MyMethod(x: int) = base.MyMethod(x) - v
+
+// []
+// type MangledAbstractClass5(v) =
+// inherit MangledAbstractClass4(v + 5)
+// override _.MyMethod(x: int) = base.MyMethod(x) + v + 7
+
+// type ConcreteClass1() =
+// inherit MangledAbstractClass5(2)
+
+// type IndexedProps(v: int) =
+// let mutable v = v
+// member _.Item with get (v2: int) = v + v2 and set v2 (s: string) = v <- v2 + int s
+// member _.Item with get (v2: float) = float v + v2 / 2.
+
+// []
+// type ITesting =
+// static member Testing x = x
+
+// type TypeWithByRefMember() =
+// static member DoubleIntByRef (x: byref) : unit = x <- 2 * x
+
+// let inline doubleIntByRef (x: ^a) (input: int) : int =
+// let mutable value = input
+// (^a: (static member DoubleIntByRef: byref -> unit) &value)
+// value
+
+// let byrefFunc(n: byref) =
+// n <- n + n
+
+// let inline callWithByrefCreatedFromByrefInlined(n: byref) =
+// let f = &n
+// byrefFunc &f
+
+// let inline inlinedFunc(n: 't[]) =
+// n.Length
+
+// let genericByrefFunc(n: byref<'t[]>) =
+// inlinedFunc n
+
+[]
+type MyOptionalClass(?arg1: float, ?arg2: string, ?arg3: int) =
+ member val P1 = defaultArg arg1 1.0
+ member val P2 = defaultArg arg2 "1"
+ member val P3 = defaultArg arg3 1
+
+type VeryOptionalInterface =
+ abstract Bar: int option
+ abstract Baz: bool option
+ abstract Bax: float option
+ abstract Wrapped: unit option
+ abstract Foo: string option with get, set
+ abstract Fn: (int -> int -> int) option
+ abstract Fn2: (int -> int -> int)
+
+// type VeryOptionalClass () =
+// let mutable foo = "ab"
+// interface VeryOptionalInterface with
+// member _.Bar = Some 3
+// member _.Baz = None
+// member _.Wrapped = Some ()
+// member _.Bax =
+// let mutable h = ["6"] |> List.tryHead
+// h |> Option.map float
+// member _.Foo
+// with get() = Some foo
+// and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+// member _.Fn = Some (+)
+// member _.Fn2 = (*)
+
+// type StaticClass =
+// []
+// static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
+// []
+// static member NamedParams(?name : string, ?age: int) =
+// let name = defaultArg name "John"
+// let age = defaultArg age 30
+// $"%s{name} is %d{age} years old"
+// static member FSharpOptionalParam(?value: bool) = defaultArg value true
+// static member FSharpOptionalParam2(?value: unit) = Option.isSome value
+// static member DefaultParam([] value: bool) = value
+// static member DefaultParam2([] value: Nullable) = if value.HasValue then value.Value + 2 else 3
+// static member DefaultNullParam([] x: obj) = x
+// static member inline InlineAdd(x: int, ?y: int) = x + (defaultArg y 2)
+
+let tests() =
+
+ testCase "Optional arguments work" <| fun () ->
+ let x = MyOptionalClass(?arg2 = Some "2")
+ (x.P1, x.P2, x.P3) |> equal (1.0, "2", 1)
+ let y = MyOptionalClass(2.0)
+ (y.P1, y.P2, y.P3) |> equal (2.0, "1", 1)
+ let z = MyOptionalClass(?arg3 = Some 2)
+ (z.P1, z.P2, z.P3) |> equal (1.0, "1", 2)
+
+// testCase "Can implement interface optional properties" <| fun () ->
+// let veryOptionalValue = VeryOptionalClass() :> VeryOptionalInterface
+// veryOptionalValue.Bar |> equal (Some 3)
+// veryOptionalValue.Baz |> Option.isSome |> equal false
+// veryOptionalValue.Wrapped |> Option.isSome |> equal true
+// veryOptionalValue.Bax |> equal (Some 6.)
+// veryOptionalValue.Foo <- Some "z"
+// veryOptionalValue.Foo |> equal (Some "abz")
+// veryOptionalValue.Foo <- None
+// veryOptionalValue.Foo |> equal (Some "abz")
+// let fn = veryOptionalValue.Fn
+// let fn2 = veryOptionalValue.Fn2
+// let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+// let f2 = match fn with Some f -> f 1 8 | None -> -5
+// f1 + f2 - fn2 9 3 |> equal -3
+
+// testCase "Can implement interface optional properties with object expression" <| fun () ->
+// let veryOptionalValue =
+// let mutable foo = "ab"
+// { new VeryOptionalInterface with
+// member _.Bar = Some 3
+// member _.Baz = None
+// member _.Wrapped = Some ()
+// member _.Bax = ["6"] |> List.tryHead |> Option.map float
+// member _.Foo
+// with get() = Some foo
+// and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+// member _.Fn = Some (+)
+// member _.Fn2 = (*)
+// }
+
+// veryOptionalValue.Bar |> equal (Some 3)
+// veryOptionalValue.Baz |> Option.isSome |> equal false
+// veryOptionalValue.Wrapped |> Option.isSome |> equal true
+// veryOptionalValue.Bax |> equal (Some 6.)
+// veryOptionalValue.Foo <- Some "z"
+// veryOptionalValue.Foo |> equal (Some "abz")
+// veryOptionalValue.Foo <- None
+// veryOptionalValue.Foo |> equal (Some "abz")
+// let fn = veryOptionalValue.Fn
+// let fn2 = veryOptionalValue.Fn2
+// let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+// let f2 = match fn with Some f -> f 1 8 | None -> -5
+// f1 + f2 - fn2 9 3 |> equal -3
+
+// testCase "Optional named params work" <| fun () ->
+// StaticClass.NamedParams(foo="5", bar=4) |> equal 9
+// StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
+// StaticClass.NamedParams(foo="14") |> equal 11
+// StaticClass.NamedParams() |> equal "John is 30 years old"
+
+// testCase "F# optional param works" <| fun () ->
+// let mutable f1 = fun (v: bool) ->
+// StaticClass.FSharpOptionalParam(value=v)
+// let mutable f2 = fun (v: bool option) ->
+// StaticClass.FSharpOptionalParam(?value=v)
+// StaticClass.FSharpOptionalParam() |> equal true
+// StaticClass.FSharpOptionalParam(true) |> equal true
+// StaticClass.FSharpOptionalParam(false) |> equal false
+// StaticClass.FSharpOptionalParam(?value=None) |> equal true
+// StaticClass.FSharpOptionalParam(?value=Some true) |> equal true
+// StaticClass.FSharpOptionalParam(?value=Some false) |> equal false
+// f1 true |> equal true
+// f1 false |> equal false
+// None |> f2 |> equal true
+// Some false |> f2 |> equal false
+// Some true |> f2 |> equal true
+
+// testCase "F# optional param works with no-wrappable options" <| fun () ->
+// let mutable f1 = fun (v: unit) ->
+// StaticClass.FSharpOptionalParam2(value=v)
+// let mutable f2 = fun (v: unit option) ->
+// StaticClass.FSharpOptionalParam2(?value=v)
+// StaticClass.FSharpOptionalParam2() |> equal false
+// StaticClass.FSharpOptionalParam2(()) |> equal true
+// StaticClass.FSharpOptionalParam2(?value=None) |> equal false
+// StaticClass.FSharpOptionalParam2(?value=Some ()) |> equal true
+// f1 () |> equal true
+// None |> f2 |> equal false
+// Some () |> f2 |> equal true
+
+// testCase "DefaultParameterValue works" <| fun () ->
+// StaticClass.DefaultParam() |> equal true
+// StaticClass.DefaultParam(true) |> equal true
+// StaticClass.DefaultParam(false) |> equal false
+
+// StaticClass.DefaultParam2(5) |> equal 7
+// StaticClass.DefaultParam2(Nullable()) |> equal 3
+// StaticClass.DefaultParam2() |> equal 3
+
+// testCase "DefaultParameterValue works with null" <| fun () -> // See #3326
+// StaticClass.DefaultNullParam() |> isNull |> equal true
+// StaticClass.DefaultNullParam(5) |> isNull |> equal false
+
+// testCase "Inlined methods can have optional arguments" <| fun () ->
+// StaticClass.InlineAdd(2, 3) |> equal 5
+// StaticClass.InlineAdd(5) |> equal 7
+
+// // TODO: This test produces different results in Fable and .NET
+// // See Fable.Transforms.FSharp2Fable.TypeHelpers.makeTypeGenArgs
+// // testCase "Reflection for types with measures work" <| fun () ->
+// // Reflection.FSharpType.GetRecordFields(typeof)
+// // |> Array.item 0
+// // |> fun fi -> fi.PropertyType.GetGenericArguments().Length
+// // |> equal 1
+
+// testCase "Indexed properties work" <| fun () ->
+// let f = IndexedProps(5)
+// f[4] |> equal 9
+// f[3] <- "6"
+// f[4] |> equal 13
+// f[4.] |> equal 11
+
+// testCase "Static interface members work" <| fun () ->
+// let a = ITesting.Testing 5
+// a |> equal 5
+
+// testCase "Types can instantiate their parent in the constructor" <| fun () ->
+// let t = TestType9()
+// t.Greet("Maxime") |> equal "Hello Maxime"
+
+// testCase "Type testing" <| fun () ->
+// let x = TestType "test" :> obj
+// let y = TestType2 "test" :> obj
+// x :? TestType |> equal true
+// x :? TestType2 |> equal false
+// y :? TestType |> equal false
+// y :? TestType2 |> equal true
+
+// testCase "Type testing in pattern matching" <| fun () ->
+// let x = TestType "test" :> obj
+// match x with
+// | :? TestType as x -> x.Value
+// | _ -> "FAIL"
+// |> equal "test"
+// match x with
+// | :? TestType2 as x -> x.Value
+// | _ -> "FAIL"
+// |> equal "FAIL"
+
+// // TODO: Should we make interface testing work in Fable 2?
+// // testCase "Children inherits parent interfaces" <| fun () ->
+// // let t4 = TestType4() |> box
+// // t4 :? ITest |> equal true
+
+// // testCase "Interface testing" <| fun () ->
+// // let x = Union1 "test" :> obj
+// // let y = Union2 "test" :> obj
+// // x :? ITest |> equal true
+// // x :? ITest2 |> equal false
+// // y :? ITest |> equal true
+// // y :? ITest2 |> equal false
+
+// // testCase "Interface testing in pattern matching" <| fun () ->
+// // let x = Union2 "test" :> obj
+// // match x with | :? ITest -> true | _ -> false
+// // |> equal true
+// // match x with | :? ITest2 -> true | _ -> false
+// // |> equal false
+
+// testCase "Type testing with JS primitive types works" <| fun () ->
+// let test (o: obj) =
+// match o with
+// | :? string -> "string"
+// | :? float -> "number"
+// | :? bool -> "boolean"
+// | :? unit -> "unit"
+// | :? System.Text.RegularExpressions.Regex -> "RegExp"
+// | :? (int[]) | :? (string[]) -> "Array"
+// | _ -> "unknown"
+// "A" :> obj |> test |> equal "string"
+// 3. :> obj |> test |> equal "number"
+// false :> obj |> test |> equal "boolean"
+// () :> obj |> test |> equal "unit"
+// // Workaround to make sure Fable is passing the argument
+// let a = () :> obj in test a |> equal "unit"
+// System.Text.RegularExpressions.Regex(".") :> obj |> test |> equal "RegExp"
+// [|"A"|] :> obj |> test |> equal "Array"
+// [|1;2|] :> obj |> test |> equal "Array"
+
+// testCase "Type test with Date" <| fun () ->
+// let isDate (x: obj) =
+// match x with
+// | :? DateTime -> true
+// | _ -> false
+// DateTime.Now |> box |> isDate |> equal true
+// box 5 |> isDate |> equal false
+
+// testCase "Type test with Long" <| fun () ->
+// let isLong (x: obj) =
+// match x with
+// | :? int64 -> true
+// | _ -> false
+// box 5L |> isLong |> equal true
+// box 50 |> isLong |> equal false
+
+// testCase "Type test with BigInt" <| fun () ->
+// let isBigInd (x: obj) =
+// match x with
+// | :? bigint -> true
+// | _ -> false
+// box 5I |> isBigInd |> equal true
+// box 50 |> isBigInd |> equal false
+
+// testCase "Property names don't clash with built-in JS objects" <| fun () -> // See #168
+// let gameState = {
+// Now = 1
+// Map = "dungeon"
+// Players = Map.empty
+// }
+// gameState.Players.ContainsKey(1) |> equal false
+
+// testCase "Overloads work" <| fun () ->
+// let t = TestType5("")
+// t.Overload(2) |> equal 4
+// t.Overload(2, 3) |> equal 5
+
+// testCase "Type abbreviation works" <| fun () ->
+// let t = T4()
+// t.Value2 |> equal "Bye"
+
+// testCase "Getter and Setter work" <| fun () ->
+// let t = TestType6(5)
+// t.Value1 |> equal 5
+// t.Value2 |> equal 10
+// t.Value3 |> equal 25
+// t.Value3 <- 10
+// t.Value1 |> equal 5
+// t.Value2 |> equal 20
+// t.Value3 |> equal 100
+// t.Value1 <- 20
+// t.Value1 |> equal 20
+// t.Value2 |> equal 20
+// t.Value3 |> equal 100
+
+// testCase "Getter and Setter with indexer work" <| fun () ->
+// let t = TestType7(1, 2, 3)
+// t.Value(1) |> equal 2
+// t.Value(2) |> equal 3
+// t.Value(1) <- 5
+// t.Value(1) |> equal 5
+// t.Value(2) |> equal 3
+
+// testCase "Attached Getters Setters and Indexers work" <| fun () ->
+// let t = TestTypeAttached(1, 2, 3)
+// t.Value1 |> equal 2
+// t.Value1 <- 22
+// t.Value1 |> equal 22
+// t.Value(0) |> equal 1
+// t.Value(0) <- 11
+// t.Value(0) |> equal 11
+// t[2] |> equal 3
+// t[2] <- 33
+// t[2] |> equal 33
+
+// testCase "Interface Getters Setters and Indexers work" <| fun () ->
+// let t = TestProps([| 1; 2; 3 |]) :> ITestProps
+// t.Value1 |> equal 2
+// t.Value1 <- 22
+// t.Value1 |> equal 22
+// t.Value(0) |> equal 1
+// t.Value(0) <- 11
+// t.Value(0) |> equal 11
+// t[2] |> equal 3
+// t[2] <- 33
+// t[2] |> equal 33
+
+// testCase "Statically resolved instance calls work" <| fun () ->
+// let a = { thing = 5 }
+// let b = { label = "five" }
+// show a |> equal "5"
+// show b |> equal "five"
+
+// testCase "Statically resolved static calls work" <| fun () ->
+// let a = { thing = 5 }
+// let b = { label = "five" }
+// showStatic a |> equal "Static: 5"
+// showStatic b |> equal "Static: five"
+
+// testCase "Guid.NewGuid works" <| fun () ->
+// let g1 = Guid.NewGuid()
+// let g2 = Guid.NewGuid()
+// g1 = g2 |> equal false
+// let s1 = string g1
+// equal 36 s1.Length
+// Text.RegularExpressions.Regex.IsMatch(
+// s1, "^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$")
+// |> equal true
+// let g3 = Guid.Parse s1
+// g1 = g3 |> equal true
+
+// testCase "Guid.Empty works" <| fun () ->
+// let g1 = Guid.Empty
+// string g1 |> equal "00000000-0000-0000-0000-000000000000"
+
+// testCase "Guid.ToString works" <| fun () ->
+// let g1 = Guid.Parse "dec42487-c02b-42a6-9a10-0263a5a7fdf1"
+// string g1 |> equal "dec42487-c02b-42a6-9a10-0263a5a7fdf1"
+
+// testCase "lazy works" <| fun () ->
+// let mutable snitch = 0
+// let lazyVal =
+// lazy
+// snitch <- snitch + 1
+// 5
+// equal 0 snitch
+// equal 5 lazyVal.Value
+// equal 1 snitch
+// lazyVal.Force() |> equal 5
+// equal 1 snitch
+
+// testCase "Lazy.CreateFromValue works" <| fun () ->
+// let mutable snitch = 0
+// let lazyVal =
+// Lazy<_>.CreateFromValue(
+// snitch <- snitch + 1
+// 5)
+// equal 1 snitch
+// equal 5 lazyVal.Value
+// equal 1 snitch
+
+// testCase "lazy.IsValueCreated works" <| fun () ->
+// let mutable snitch = 0
+// let lazyVal =
+// Lazy<_>.Create(fun () ->
+// snitch <- snitch + 1
+// 5)
+// equal 0 snitch
+// equal false lazyVal.IsValueCreated
+// equal 5 lazyVal.Value
+// equal true lazyVal.IsValueCreated
+// lazyVal.Force() |> equal 5
+// equal true lazyVal.IsValueCreated
+
+// testCase "Lazy constructor works" <| fun () ->
+// let items = Lazy(fun () -> ["a";"b";"c"])
+// let search e = items.Value |> List.tryFind (fun m -> m = e)
+// search "b" |> equal (Some "b")
+// search "d" |> equal None
+
+// testCase "Secondary constructors work" <| fun () ->
+// let s1 = SecondaryCons(3)
+// let s2 = SecondaryCons()
+// equal 3 s1.Value
+// equal 5 s2.Value
+
+// // testCase "Inheriting from secondary constructors works" <| fun () ->
+// // let s = SecondaryConsChild()
+// // equal 5 s.Value
+
+// testCase "Multiple constructors work" <| fun () ->
+// let m1 = MultipleCons()
+// let m2 = MultipleCons(5)
+// let m3 = MultipleCons(7,7)
+// equal 5 m1.Value
+// equal 9 m2.Value
+// equal 14 m3.Value
+
+// testCase "Abstract methods with default work" <| fun () -> // See #505
+// let x = ConcreteClass()
+// x.MethodWithDefault() |> equal "Hello "
+// x.MustImplement() |> equal "World!!"
+// x.CallMethodWithDefault() |> equal "Hello World!!"
+// let x = ConcreteClass2()
+// x.CallMethodWithDefault() |> equal "Hi World!!"
+
+// testCase "Abstract properties with getters and setters work" <| fun () ->
+// let x = ConcreteClass3() :> AbstractClass3
+// x.MyProp <- 2
+// equal 7 x.MyProp
+
+// testCase "Interface setters don't conflict" <| fun () -> // See #505
+// let x = XISomeInterface () :> ISomeInterface
+// x.Sender |> equal 0
+// x.Sender <- 5
+// x.Sender |> equal 5
+
+// testCase "A type can overload an interface method" <| fun () ->
+// let foo = FooImplementor()
+// foo.Foo() |> equal "foo"
+// (foo :> IFoo).Foo() |> equal "foobar"
+// mangleFoo foo |> equal "foobar"
+
+// testCase "A child can be casted to parent's interface" <| fun () ->
+// let foo = FooImplementorChild()
+// foo.Foo() |> equal "foofoofoo"
+// (foo :> IFoo).Foo() |> equal "foofoofoobar"
+// mangleFoo foo |> equal "foofoofoobar"
+
+// testCase "A type can overload an interface getter" <| fun () ->
+// let foo = FooImplementor()
+// foo.Bar |> equal "he"
+// (foo :> IFoo).Bar |> equal "heho"
+
+// testCase "A type can overload an interface setter" <| fun () ->
+// let foo = FooImplementor()
+// foo.MySetter <- 7
+// foo.MySetter |> equal 9
+// (foo :> IFoo).MySetter <- 7
+// (foo :> IFoo).MySetter |> equal 19
+
+// // TODO: Interface and abstract methods with same name clash
+// testCase "A type overloading an interface method can be inherited" <| fun () ->
+// let foo = ChildFoo() :> AbstractFoo
+// foo.Foo2() |> equal "BAR"
+// (foo :> IFoo).Foo() |> equal "BARFOO"
+// mangleFoo foo |> equal "BARFOO"
+
+// testCase "Interface casting round-trip" <| fun () -> // See #1452
+// let d = new DowncastTest(3) :> System.IDisposable
+// let t = d :?> DowncastTest
+// t.Value |> equal 3
+// equal 3 <|
+// match d with
+// | :? DowncastTest as t2 -> t2.Value
+// | _ -> 5
+
+// testCase "Calling default implementation of base members don't cause infinite recursion" <| fun () -> // See #701
+// let x = ExtendedClass()
+// x.Init() |> equal 7
+// (x :> BaseClass).Init() |> equal 7
+
+// testCase "Calling default implementation of base properties don't cause infinite recursion" <| fun () -> // See #701
+// let x = ExtendedClass()
+// x.Prop |> equal "base-extension"
+// (x :> BaseClass).Prop |> equal "base-extension"
+
+// testCase "Calling base members works" <| fun () -> // See #1464
+// let bar = ExtendedClass2()
+// bar.B() |> equal 1
+
+// testCase "Circular dependencies work" <| fun () -> // See #569
+// let location = { name="NY"; employees=[] }
+// let alice = { name="Alice"; age=20.0; location=location }
+// location.name |> equal "NY"
+// alice.age |> equal 20.
+
+// testCase "Value Type records work" <| fun () -> // See #568
+// let foo1 = ValueType<_>("foo")
+// let foo2 = ValueType<_>("foo")
+// foo1.Value |> equal "foo"
+// foo1.value |> equal "foo"
+// foo1 = foo2 |> equal true
+
+// testCase "Value Type unions work" <| fun () ->
+// let du1 = StructUnion.Value "du"
+// let du2 = StructUnion.Value "du"
+// du1 = du2 |> equal true
+
+// testCase "Value Type tuples work" <| fun () ->
+// let tu1 = struct ("a","b")
+// let tu2 = struct ("a","b")
+// tu1 = tu2 |> equal true
+
+// testCase "Value Types work" <| fun () ->
+// let bar1 = ValueType1("bar")
+// let bar2 = ValueType1("bar")
+// bar1.Value |> equal "bar"
+// bar1 = bar2 |> equal true
+
+// testCase "Other Value Types work" <| fun () ->
+// let test2 = ValueType2(3, 4)
+// test2.Value |> equal 7
+// let p = Point2D(2.)
+// p.Y |> equal 2.
+
+// testCase "struct without explicit ctor works" <| fun () ->
+// let t1 = ValueType3(X=10)
+// t1.X |> equal 10
+// let mutable t2 = ValueType3()
+// t2.X |> equal 0
+// t1 |> notEqual t2
+// (compare t1 t2) |> equal 1
+// t2.X <- 10
+// t1 |> equal t2
+// (compare t1 t2) |> equal 0
+
+// testCase "copying struct records works" <| fun () -> // See #3371
+// let simple : SimpleRecord = { A = ""; B = "B" }
+// let simpleRecord = { simple with A = "A" }
+// simpleRecord.A |> equal "A"
+// simpleRecord.B |> equal "B"
+// simple.A |> equal ""
+// simple.B |> equal "B"
+
+// testCase "Custom F# exceptions work" <| fun () ->
+// try
+// MyEx(4,"ERROR") |> raise
+// with
+// | MyEx(4, msg) as e -> (box e :? Exception, msg + "!!")
+// | MyEx(_, msg) as e -> (box e :? Exception, msg + "??")
+// | ex -> (false, "unknown")
+// |> equal (true, "ERROR!!")
+
+// testCase "Custom exceptions work" <| fun () ->
+// try
+// MyEx2(5.5) |> raise
+// with
+// | :? MyEx2 as ex -> (box ex :? Exception, ex.Message, ex.Code)
+// | ex -> (false, "unknown", 0.)
+// |> equal (true, "Code: 5", 5.5)
+
+// testCase "reraise works" <| fun () ->
+// try
+// try
+// Exception("Will I be reraised?") |> raise
+// with _ ->
+// try
+// reraise()
+// with _ -> reraise()
+// "foo"
+// with ex -> ex.Message
+// |> equal "Will I be reraised?"
+
+// testCase "This context is not lost in closures within implicit constructor" <| fun () -> // See #1444
+// ThisContextInConstructor(7).Value() |> equal 7
+
+// testCase "Type can be casted to an interface implemented by parent" <| fun () ->
+// let c = TestType10Child()
+// let f1 = c :> ITest4
+// let f2 = c :> ITest3
+// f1.Add(4, 5) |> equal 9
+// f1.Add2(4, 5) |> equal -1
+// f2.Add2(4, 5) |> equal -1
+
+// testCase "ClassAttribute works" <| fun () -> // See #573
+// let t1 = TypeWithClassAttribute(8)
+// t1.Pos |> equal 8
+
+// testCase "Issue #1975: Compile type with parameterized units of measure as generic" <| fun () ->
+// let a = makeTestTypeWithParameterizedUnitMeasureType 2.
+// equal 2. a.Value
+
+// // Test ported from https://github.com/fable-compiler/Fable/pull/1336/files
+// // testCase "default value attributes works" <| fun _ ->
+// // let withDefaultValue = TestTypeWithDefaultValue()
+// //
+// // withDefaultValue.IntValue |> equal Unchecked.defaultof
+// // withDefaultValue.IntValue |> equal 0
+// //
+// // withDefaultValue.StringValue |> equal Unchecked.defaultof
+// // withDefaultValue.StringValue |> equal null
+// //
+// // withDefaultValue.ObjValue |> equal Unchecked.defaultof>
+// // withDefaultValue.ObjValue |> equal null
+
+// testCase "Private fields don't conflict with parent classes" <| fun _ -> // See #2070
+// let a1 = InfoBClass({ InfoA = { Foo = "foo" }; Bar = "bar" }) :> InfoAClass
+// let a2 = a1.WithFoo("foo2")
+// a1.Foo |> equal "foo"
+// a2.Foo |> equal "foo2"
+
+// // See #2084
+// testCase "Non-mangled interfaces work with object expressions" <| fun _ ->
+// let mutable foo = "Foo"
+// let foo = { new FooInterface with
+// member _.Foo with get() = foo and set x = foo <- x
+// member _.DoSomething(f, x) = let f = f 1. in f x * f 0.2
+// member _.Item with get(i) = foo.[i] and set i c = foo <- FooClass.ChangeChar(foo, i - 1, c)
+// member _.Sum(items) = Array.reduce (+) items }
+
+// let addPlus2 x y = x + y + 2.
+// let multiplyTwice x y = x * y * y
+
+// foo.[3] <- 'W'
+// foo.Foo <- foo.Foo + foo.DoSomething(addPlus2, 3.).ToString("F2").Replace(",", ".") + foo.[2].ToString()
+// foo.Foo <- foo.Foo + foo.Sum("a", "bc", "d")
+
+// foo.Foo |> equal "FoW19.20Wabcd"
+
+// // See #2084
+// testCase "Mangled interfaces work with object expressions" <| fun _ ->
+// let mutable bar = "Bar"
+// let bar = { new BarInterface with
+// member _.Bar with get() = bar and set x = bar <- x
+// member _.DoSomething(f, x) = let f = f 4.3 in f x + f x
+// member _.Item with get(i) = bar.[i] and set _ c = bar <- FooClass.ChangeChar(bar, 0, c)
+// member _.Item with get(c) = bar.ToCharArray() |> Array.exists ((=) c)
+// member _.Sum(items) = Array.rev items |> Array.reduce (+) }
+
+// let addPlus2 x y = x + y + 2.
+// let multiplyTwice x y = x * y * y
+
+// bar.[3] <- 'Z'
+// bar.Bar <- bar.Bar + bar.DoSomething(multiplyTwice, 3.).ToString("F2").Replace(",", ".") + bar.[2].ToString() + (sprintf "%b%b" bar.['B'] bar.['x'])
+// bar.Bar <- bar.Bar + bar.Sum("a", "bc", "d")
+
+// bar.Bar |> equal "Zar77.40rfalsefalsedbca"
+
+// // See #2084
+// testCase "Non-mangled interfaces work with classes" <| fun _ ->
+// let addPlus2 x y = x + y + 2.
+// let multiplyTwice x y = x * y * y
+// let foo2 = FooClass("Foo") :> FooInterface
+// foo2.[0] <- 'W'
+// foo2.Foo <- foo2.Foo + foo2.DoSomething(multiplyTwice, 3.).ToString("F2").Replace(',', '.') + foo2.[2].ToString()
+// foo2.Foo <- foo2.Foo + foo2.Sum("a", "bc", "d")
+// foo2.Foo |> equal "Woo1020.00oabcabcdabcabcd"
+
+// // See #2084
+// testCase "Mangled interfaces work with classes" <| fun _ ->
+// let addPlus2 x y = x + y + 2.
+// let multiplyTwice x y = x * y * y
+// let bar2 = BarClass("Bar") :> BarInterface
+// bar2.[0] <- 'Z'
+// bar2.Bar <- bar2.Bar + bar2.DoSomething(addPlus2, 3.).ToString("F2").Replace(",", ".") + bar2.[2].ToString() + (sprintf "%b%b" bar2.['B'] bar2.['x'])
+// bar2.Bar <- bar2.Bar + bar2.Sum("a", "bc", "d")
+// bar2.Bar |> equal "BZr9536.74rtruefalseaabcbcaabcbcdd"
+
+// testCase "Multiple `this` references work in nested attached members" <| fun _ ->
+// (MixedThese(2) :> Interface1).Create(3).Add() |> equal 5
+
+// testCase "Two unions of different type with same shape are not equal" <| fun () ->
+// areEqual (MyUnion1.Foo(1,2)) (MyUnion2.Foo(1,2)) |> equal false
+// areEqual (MyUnion1.Foo(1,2)) (MyUnion1.Foo(1,2)) |> equal true
+
+// testCase "Two records of different type with same shape are not equal" <| fun () ->
+// areEqual { MyRecord1.Foo = 2; Bar = "oh" } { MyRecord2.Foo = 2; Bar = "oh" } |> equal false
+// areEqual { MyRecord1.Foo = 2; Bar = "oh" } { MyRecord1.Foo = 2; Bar = "oh" } |> equal true
+
+// testCase "IsSubclassOf checks whole hierarchy" <| fun () ->
+// typeof.IsSubclassOf(typeof) |> equal true
+// typeof.IsSubclassOf(typeof) |> equal true
+
+// testCase "IsSubclassOf returns true for System.Object" <| fun () ->
+// typeof.IsSubclassOf(typeof) |> equal true
+// typeof.IsSubclassOf(typeof) |> equal true
+// typeof.IsSubclassOf(typeof) |> equal true
+// typeof unit>.IsSubclassOf(typeof) |> equal true
+
+// testCase "IsInstanceOfType works with class types" <| fun () ->
+// let s1, s2 = SubclassTest1(), SubclassTest2()
+// typeof.IsInstanceOfType(s1) |> equal true
+// typeof.IsInstanceOfType(s1) |> equal true
+// typeof.IsInstanceOfType(s1) |> equal false
+// typeof.IsInstanceOfType(s1) |> equal false
+// typeof.IsInstanceOfType(s2) |> equal true
+// typeof.IsInstanceOfType(s2) |> equal true
+// typeof.IsInstanceOfType(s2) |> equal false
+
+// testCase "IsInstanceOfType works with nominal records" <| fun () ->
+// typeof.IsInstanceOfType({ MyRecord1.Foo = 2; Bar = "oh" }) |> equal true
+// typeof.IsInstanceOfType({ MyRecord1.Foo = 2; Bar = "oh" }) |> equal true
+// typeof.IsInstanceOfType({ MyRecord1.Foo = 2; Bar = "oh" }) |> equal false
+
+// testCase "IsInstanceOfType works with nominal unions" <| fun () ->
+// typeof.IsInstanceOfType(MyUnion1.Foo(1,2)) |> equal true
+// typeof.IsInstanceOfType(MyUnion1.Foo(1,2)) |> equal true
+// typeof.IsInstanceOfType(MyUnion1.Foo(1,2)) |> equal false
+
+// // Expected to always return true for any numeric type, just like :? operator
+// testCase "IsInstanceOfType works with enums" <| fun () ->
+// typeof.IsInstanceOfType(EnumFoo.Foo) |> equal true
+// typeof.IsInstanceOfType(EnumFoo.Bar) |> equal true
+// typeof.IsInstanceOfType(EnumFoo.Bar) |> equal true
+
+// // Expected to always return true for any function and function type, just like :? operator
+// testCase "IsInstanceOfType works with functions" <| fun () ->
+// typeof unit>.IsInstanceOfType(fun () -> ()) |> equal true
+// typeof.IsInstanceOfType(fun () -> ()) |> equal true
+// //typeof int>.IsInstanceOfType(fun () -> ()) |> equal false
+// typeof int>.IsInstanceOfType(String.length) |> equal true
+// typeof.IsInstanceOfType(String.length) |> equal true
+// //typeof int>.IsInstanceOfType(String.length) |> equal false
+
+// testCase "IsInstanceOfType works with primitives" <| fun () ->
+// typeof.IsInstanceOfType("hello") |> equal true
+// typeof.IsInstanceOfType("hello") |> equal true
+// typeof.IsInstanceOfType(5) |> equal false
+// typeof.IsInstanceOfType(5) |> equal true
+// typeof.IsInstanceOfType(5) |> equal true
+// typeof.IsInstanceOfType("hello") |> equal false
+
+// #if FABLE_COMPILER
+// testCase "Choice with arity 3+ is represented correctly" <| fun () -> // See #2485
+// Choice2Of3 55 |> Fable.Core.Reflection.getCaseName |> equal "Choice2Of3"
+// Choice3Of3 55 |> Fable.Core.Reflection.getCaseName |> equal "Choice3Of3"
+// #endif
+
+// testCase "Can call the base version of a mangled abstract method that was declared above in the hierarchy" <| fun () ->
+// let c = ConcreteClass1()
+// c.MyMethod(4) |> equal 58
+
+// // See #3328
+// testCase "SRTP works with byref" <| fun () ->
+// let result = doubleIntByRef (TypeWithByRefMember()) 7
+// result |> equal 14
+
+// // See #3328
+// testCase "inline byref works" <| fun () ->
+// let mutable an_int = 22
+// callWithByrefCreatedFromByrefInlined &an_int
+// an_int |> equal 44
+
+// // See #3328
+// testCase "inline byref works (with separate binding for reference)" <| fun () ->
+// let mutable an_int = 33
+// let intRef = &an_int
+// ignore intRef
+// callWithByrefCreatedFromByrefInlined &intRef
+// an_int |> equal 66
+
+// testCase "inline with generic byref works" <| fun () ->
+// let mutable arr = [| 1; 2; 3 |]
+// let result = genericByrefFunc &arr
+// result |> equal 3
diff --git a/tests/Js/Main/OptionTests.fs b/tests/Js/Main/OptionTests.fs
index 1b8aa03b8f..363062e631 100644
--- a/tests/Js/Main/OptionTests.fs
+++ b/tests/Js/Main/OptionTests.fs
@@ -1,8 +1,6 @@
module Fable.Tests.Option
open System
-open System.Runtime.InteropServices
-open Fable.Core
open Fable.Core.JsInterop
open Util.Testing
@@ -35,167 +33,8 @@ type OptTest = OptTest of int option
let makeSome (x: 'a): 'a option =
Some x
-type VeryOptionalInterface =
- abstract Bar: int option
- abstract Baz: bool option
- abstract Bax: float option
- abstract Wrapped: unit option
- abstract Foo: string option with get, set
- abstract Fn: (int -> int -> int) option
- abstract Fn2: (int -> int -> int)
-
-type VeryOptionalClass () =
- let mutable foo = "ab"
- interface VeryOptionalInterface with
- member _.Bar = Some 3
- member _.Baz = None
- member _.Wrapped = Some ()
- member _.Bax =
- let mutable h = ["6"] |> List.tryHead
- h |> Option.map float
- member _.Foo
- with get() = Some foo
- and set(v) = foo <- match v with Some v -> foo + v | None -> foo
- member _.Fn = Some (+)
- member _.Fn2 = (*)
-
-type StaticClass =
- []
- static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
- []
- static member NamedParams(?name : string, ?age: int) =
- let name = defaultArg name "John"
- let age = defaultArg age 30
-
- $"%s{name} is %d{age} years old"
- static member FSharpOptionalParam(?value: bool) = defaultArg value true
- static member FSharpOptionalParam2(?value: unit) = Option.isSome value
- static member DefaultParam([] value: bool) = value
- static member DefaultParam2([] value: Nullable) = if value.HasValue then value.Value + 2 else 3
- static member DefaultNullParam([] x: obj) = x
- static member inline InlineAdd(x: int, ?y: int) = x + (defaultArg y 2)
-
let tests =
testList "Option" [
- testCase "Anonymous records can have optional fields" <| fun () ->
- let add (o: {| bar: int option; zas: string option; foo: int option option |}) =
- let bar = o.bar |> Option.map string |> Option.defaultValue "-"
- let zas = defaultArg o.zas ""
- let foo = match o.foo with Some(Some i) -> string i | Some None -> "xx" | None -> "x"
- bar + zas + foo
-
- {| bar = Some 3; zas = Some "ooooo"; foo = Some None |} |> add |> equal "3oooooxx"
- {| bar = Some 22; zas = Some ""; foo = Some(Some 999) |} |> add |> equal "22999"
- {| bar = None; zas = None; foo = None |} |> add |> equal "-x"
- {| foo = Some None; bar = None; zas = None |} |> add |> equal "-xx"
-
- testCase "Anonymous records can have optional function fields" <| fun () ->
- let add (o: {| bar: (int -> int -> int) option; foo: int -> int -> int |}) =
- let fn = o.bar
- let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
- let f2 = match fn with Some f -> f 1 8 | None -> -5
- o.foo 3 4 + f1 + f2
-
- {| bar = Some (+); foo = (*) |} |> add |> equal 36
- {| bar = None; foo = (+) |} |> add |> equal -1
-
- testCase "Can implement interface optional properties" <| fun () ->
- let veryOptionalValue = VeryOptionalClass() :> VeryOptionalInterface
- veryOptionalValue.Bar |> equal (Some 3)
- veryOptionalValue.Baz |> Option.isSome |> equal false
- veryOptionalValue.Wrapped |> Option.isSome |> equal true
- veryOptionalValue.Bax |> equal (Some 6.)
- veryOptionalValue.Foo <- Some "z"
- veryOptionalValue.Foo |> equal (Some "abz")
- veryOptionalValue.Foo <- None
- veryOptionalValue.Foo |> equal (Some "abz")
- let fn = veryOptionalValue.Fn
- let fn2 = veryOptionalValue.Fn2
- let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
- let f2 = match fn with Some f -> f 1 8 | None -> -5
- f1 + f2 - fn2 9 3 |> equal -3
-
- testCase "Can implement interface optional properties with object expression" <| fun () ->
- let veryOptionalValue =
- let mutable foo = "ab"
- { new VeryOptionalInterface with
- member _.Bar = Some 3
- member _.Baz = None
- member _.Wrapped = Some ()
- member _.Bax = ["6"] |> List.tryHead |> Option.map float
- member _.Foo
- with get() = Some foo
- and set(v) = foo <- match v with Some v -> foo + v | None -> foo
- member _.Fn = Some (+)
- member _.Fn2 = (*)
- }
-
- veryOptionalValue.Bar |> equal (Some 3)
- veryOptionalValue.Baz |> Option.isSome |> equal false
- veryOptionalValue.Wrapped |> Option.isSome |> equal true
- veryOptionalValue.Bax |> equal (Some 6.)
- veryOptionalValue.Foo <- Some "z"
- veryOptionalValue.Foo |> equal (Some "abz")
- veryOptionalValue.Foo <- None
- veryOptionalValue.Foo |> equal (Some "abz")
- let fn = veryOptionalValue.Fn
- let fn2 = veryOptionalValue.Fn2
- let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
- let f2 = match fn with Some f -> f 1 8 | None -> -5
- f1 + f2 - fn2 9 3 |> equal -3
-
- testCase "Optional named params work" <| fun () ->
- StaticClass.NamedParams(foo="5", bar=4) |> equal 9
- StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
- StaticClass.NamedParams(foo="14") |> equal 11
- StaticClass.NamedParams() |> equal "John is 30 years old"
-
- testCase "F# optional param works" <| fun () ->
- let mutable f1 = fun (v: bool) ->
- StaticClass.FSharpOptionalParam(value=v)
- let mutable f2 = fun (v: bool option) ->
- StaticClass.FSharpOptionalParam(?value=v)
- StaticClass.FSharpOptionalParam() |> equal true
- StaticClass.FSharpOptionalParam(true) |> equal true
- StaticClass.FSharpOptionalParam(false) |> equal false
- StaticClass.FSharpOptionalParam(?value=None) |> equal true
- StaticClass.FSharpOptionalParam(?value=Some true) |> equal true
- StaticClass.FSharpOptionalParam(?value=Some false) |> equal false
- f1 true |> equal true
- f1 false |> equal false
- None |> f2 |> equal true
- Some false |> f2 |> equal false
- Some true |> f2 |> equal true
-
- testCase "F# optional param works with no-wrappable options" <| fun () ->
- let mutable f1 = fun (v: unit) ->
- StaticClass.FSharpOptionalParam2(value=v)
- let mutable f2 = fun (v: unit option) ->
- StaticClass.FSharpOptionalParam2(?value=v)
- StaticClass.FSharpOptionalParam2() |> equal false
- StaticClass.FSharpOptionalParam2(()) |> equal true
- StaticClass.FSharpOptionalParam2(?value=None) |> equal false
- StaticClass.FSharpOptionalParam2(?value=Some ()) |> equal true
- f1 () |> equal true
- None |> f2 |> equal false
- Some () |> f2 |> equal true
-
- testCase "DefaultParameterValue works" <| fun () ->
- StaticClass.DefaultParam() |> equal true
- StaticClass.DefaultParam(true) |> equal true
- StaticClass.DefaultParam(false) |> equal false
-
- StaticClass.DefaultParam2(5) |> equal 7
- StaticClass.DefaultParam2(Nullable()) |> equal 3
- StaticClass.DefaultParam2() |> equal 3
-
- testCase "DefaultParameterValue works with null" <| fun () -> // See #3326
- StaticClass.DefaultNullParam() |> isNull |> equal true
- StaticClass.DefaultNullParam(5) |> isNull |> equal false
-
- testCase "Inlined methods can have optional arguments" <| fun () ->
- StaticClass.InlineAdd(2, 3) |> equal 5
- StaticClass.InlineAdd(5) |> equal 7
testCase "defaultArg works" <| fun () ->
let f o = defaultArg o 5
diff --git a/tests/Js/Main/RecordTypeTests.fs b/tests/Js/Main/RecordTypeTests.fs
index 6e6299af5a..3a5f7ebee6 100644
--- a/tests/Js/Main/RecordTypeTests.fs
+++ b/tests/Js/Main/RecordTypeTests.fs
@@ -58,6 +58,28 @@ let tests =
// x = {| y with Baz = 23 |} |> equal true // Doesn't compile
x = {| y with Bar = 14 |} |> equal false
+ testCase "Anonymous records can have optional fields" <| fun () ->
+ let add (o: {| bar: int option; zas: string option; foo: int option option |}) =
+ let bar = o.bar |> Option.map string |> Option.defaultValue "-"
+ let zas = defaultArg o.zas ""
+ let foo = match o.foo with Some(Some i) -> string i | Some None -> "xx" | None -> "x"
+ bar + zas + foo
+
+ {| bar = Some 3; zas = Some "ooooo"; foo = Some None |} |> add |> equal "3oooooxx"
+ {| bar = Some 22; zas = Some ""; foo = Some(Some 999) |} |> add |> equal "22999"
+ {| bar = None; zas = None; foo = None |} |> add |> equal "-x"
+ {| foo = Some None; bar = None; zas = None |} |> add |> equal "-xx"
+
+ testCase "Anonymous records can have optional function fields" <| fun () ->
+ let add (o: {| bar: (int -> int -> int) option; foo: int -> int -> int |}) =
+ let fn = o.bar
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ o.foo 3 4 + f1 + f2
+
+ {| bar = Some (+); foo = (*) |} |> add |> equal 36
+ {| bar = None; foo = (+) |} |> add |> equal -1
+
testCase "SRTP works with anonymous records" <| fun () ->
let ar = [| {|Id=Id"foo"; Name="Sarah"|}; {|Id=Id"bar"; Name="James"|} |]
replaceById {|Id=Id"ja"; Name="Voll"|} ar |> Seq.head |> fun x -> equal "Sarah" x.Name
diff --git a/tests/Js/Main/TypeTests.fs b/tests/Js/Main/TypeTests.fs
index c12df3ba77..accdb92c1a 100644
--- a/tests/Js/Main/TypeTests.fs
+++ b/tests/Js/Main/TypeTests.fs
@@ -1,6 +1,8 @@
module Fable.Tests.TypeTests
open System
+open System.Runtime.InteropServices
+open Fable.Core
open Util.Testing
// Check if custom attributes can be created
@@ -530,8 +532,160 @@ let inline inlinedFunc(n: 't[]) =
let genericByrefFunc(n: byref<'t[]>) =
inlinedFunc n
+[]
+type MyOptionalClass(?arg1: float, ?arg2: string, ?arg3: int) =
+ member val P1 = defaultArg arg1 1.0
+ member val P2 = defaultArg arg2 "1"
+ member val P3 = defaultArg arg3 1
+
+type VeryOptionalInterface =
+ abstract Bar: int option
+ abstract Baz: bool option
+ abstract Bax: float option
+ abstract Wrapped: unit option
+ abstract Foo: string option with get, set
+ abstract Fn: (int -> int -> int) option
+ abstract Fn2: (int -> int -> int)
+
+type VeryOptionalClass () =
+ let mutable foo = "ab"
+ interface VeryOptionalInterface with
+ member _.Bar = Some 3
+ member _.Baz = None
+ member _.Wrapped = Some ()
+ member _.Bax =
+ let mutable h = ["6"] |> List.tryHead
+ h |> Option.map float
+ member _.Foo
+ with get() = Some foo
+ and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+ member _.Fn = Some (+)
+ member _.Fn2 = (*)
+
+type StaticClass =
+ []
+ static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
+ []
+ static member NamedParams(?name : string, ?age: int) =
+ let name = defaultArg name "John"
+ let age = defaultArg age 30
+ $"%s{name} is %d{age} years old"
+ static member FSharpOptionalParam(?value: bool) = defaultArg value true
+ static member FSharpOptionalParam2(?value: unit) = Option.isSome value
+ static member DefaultParam([] value: bool) = value
+ static member DefaultParam2([] value: Nullable) = if value.HasValue then value.Value + 2 else 3
+ static member DefaultNullParam([] x: obj) = x
+ static member inline InlineAdd(x: int, ?y: int) = x + (defaultArg y 2)
+
let tests =
testList "Types" [
+
+ testCase "Optional arguments work" <| fun () ->
+ let x = MyOptionalClass(?arg2 = Some "2")
+ (x.P1, x.P2, x.P3) |> equal (1.0, "2", 1)
+ let y = MyOptionalClass(2.0)
+ (y.P1, y.P2, y.P3) |> equal (2.0, "1", 1)
+ let z = MyOptionalClass(?arg3 = Some 2)
+ (z.P1, z.P2, z.P3) |> equal (1.0, "1", 2)
+
+ testCase "Can implement interface optional properties" <| fun () ->
+ let veryOptionalValue = VeryOptionalClass() :> VeryOptionalInterface
+ veryOptionalValue.Bar |> equal (Some 3)
+ veryOptionalValue.Baz |> Option.isSome |> equal false
+ veryOptionalValue.Wrapped |> Option.isSome |> equal true
+ veryOptionalValue.Bax |> equal (Some 6.)
+ veryOptionalValue.Foo <- Some "z"
+ veryOptionalValue.Foo |> equal (Some "abz")
+ veryOptionalValue.Foo <- None
+ veryOptionalValue.Foo |> equal (Some "abz")
+ let fn = veryOptionalValue.Fn
+ let fn2 = veryOptionalValue.Fn2
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ f1 + f2 - fn2 9 3 |> equal -3
+
+ testCase "Can implement interface optional properties with object expression" <| fun () ->
+ let veryOptionalValue =
+ let mutable foo = "ab"
+ { new VeryOptionalInterface with
+ member _.Bar = Some 3
+ member _.Baz = None
+ member _.Wrapped = Some ()
+ member _.Bax = ["6"] |> List.tryHead |> Option.map float
+ member _.Foo
+ with get() = Some foo
+ and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+ member _.Fn = Some (+)
+ member _.Fn2 = (*)
+ }
+
+ veryOptionalValue.Bar |> equal (Some 3)
+ veryOptionalValue.Baz |> Option.isSome |> equal false
+ veryOptionalValue.Wrapped |> Option.isSome |> equal true
+ veryOptionalValue.Bax |> equal (Some 6.)
+ veryOptionalValue.Foo <- Some "z"
+ veryOptionalValue.Foo |> equal (Some "abz")
+ veryOptionalValue.Foo <- None
+ veryOptionalValue.Foo |> equal (Some "abz")
+ let fn = veryOptionalValue.Fn
+ let fn2 = veryOptionalValue.Fn2
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ f1 + f2 - fn2 9 3 |> equal -3
+
+ testCase "Optional named params work" <| fun () ->
+ StaticClass.NamedParams(foo="5", bar=4) |> equal 9
+ StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
+ StaticClass.NamedParams(foo="14") |> equal 11
+ StaticClass.NamedParams() |> equal "John is 30 years old"
+
+ testCase "F# optional param works" <| fun () ->
+ let mutable f1 = fun (v: bool) ->
+ StaticClass.FSharpOptionalParam(value=v)
+ let mutable f2 = fun (v: bool option) ->
+ StaticClass.FSharpOptionalParam(?value=v)
+ StaticClass.FSharpOptionalParam() |> equal true
+ StaticClass.FSharpOptionalParam(true) |> equal true
+ StaticClass.FSharpOptionalParam(false) |> equal false
+ StaticClass.FSharpOptionalParam(?value=None) |> equal true
+ StaticClass.FSharpOptionalParam(?value=Some true) |> equal true
+ StaticClass.FSharpOptionalParam(?value=Some false) |> equal false
+ f1 true |> equal true
+ f1 false |> equal false
+ None |> f2 |> equal true
+ Some false |> f2 |> equal false
+ Some true |> f2 |> equal true
+
+ testCase "F# optional param works with no-wrappable options" <| fun () ->
+ let mutable f1 = fun (v: unit) ->
+ StaticClass.FSharpOptionalParam2(value=v)
+ let mutable f2 = fun (v: unit option) ->
+ StaticClass.FSharpOptionalParam2(?value=v)
+ StaticClass.FSharpOptionalParam2() |> equal false
+ StaticClass.FSharpOptionalParam2(()) |> equal true
+ StaticClass.FSharpOptionalParam2(?value=None) |> equal false
+ StaticClass.FSharpOptionalParam2(?value=Some ()) |> equal true
+ f1 () |> equal true
+ None |> f2 |> equal false
+ Some () |> f2 |> equal true
+
+ testCase "DefaultParameterValue works" <| fun () ->
+ StaticClass.DefaultParam() |> equal true
+ StaticClass.DefaultParam(true) |> equal true
+ StaticClass.DefaultParam(false) |> equal false
+
+ StaticClass.DefaultParam2(5) |> equal 7
+ StaticClass.DefaultParam2(Nullable()) |> equal 3
+ StaticClass.DefaultParam2() |> equal 3
+
+ testCase "DefaultParameterValue works with null" <| fun () -> // See #3326
+ StaticClass.DefaultNullParam() |> isNull |> equal true
+ StaticClass.DefaultNullParam(5) |> isNull |> equal false
+
+ testCase "Inlined methods can have optional arguments" <| fun () ->
+ StaticClass.InlineAdd(2, 3) |> equal 5
+ StaticClass.InlineAdd(5) |> equal 7
+
// TODO: This test produces different results in Fable and .NET
// See Fable.Transforms.FSharp2Fable.TypeHelpers.makeTypeGenArgs
// testCase "Reflection for types with measures work" <| fun () ->
diff --git a/tests/Python/TestMisc.fs b/tests/Python/TestMisc.fs
index 52c4117df3..ee3a52a958 100644
--- a/tests/Python/TestMisc.fs
+++ b/tests/Python/TestMisc.fs
@@ -3,7 +3,6 @@ module Fable.Tests.Misc
#nowarn "40" // warning FSHARP: This and other recursive references to the object(s) being defined will be checked for initialization-soundness at runtime through the use of a delayed reference.
open System
-open System.Runtime.InteropServices
open Fable.Core
open Util.Testing
open Util2.Extensions
@@ -416,12 +415,6 @@ type Shape =
| Square of int
| Rectangle of int * int
-type StaticClass =
- static member DefaultParam([] value: bool) = value
-
- static member inline Add(x: int, ?y: int) =
- x + (defaultArg y 2)
-
type ValueType =
struct
val public X : int
@@ -1183,15 +1176,6 @@ let ``test While with isNone doesn't hang with Some ()`` () =
let ``test Removing optional arguments not in tail position works`` () =
Internal.MyType.Add(y=6) |> equal 26
-[]
-let ``test Inlined methods can have optional arguments`` () =
- StaticClass.Add(2, 3) |> equal 5
- StaticClass.Add(5) |> equal 7
-
-[]
-let ``test DefaultParameterValue works`` () =
- StaticClass.DefaultParam() |> equal true
-
[]
let ``test Ignore shouldn't return value`` () = // See #1360
let producer () = 7
diff --git a/tests/Python/TestRecordType.fs b/tests/Python/TestRecordType.fs
index 0bce278723..214b12a8e8 100644
--- a/tests/Python/TestRecordType.fs
+++ b/tests/Python/TestRecordType.fs
@@ -56,6 +56,30 @@ let ``test Anonymous records work`` () =
// x = {| y with Baz = 23 |} |> equal true // Doesn't compile
x = {| y with Bar = 14 |} |> equal false
+[]
+let ``test Anonymous records can have optional fields`` () =
+ let add (o: {| bar: int option; zas: string option; foo: int option option |}) =
+ let bar = o.bar |> Option.map string |> Option.defaultValue "-"
+ let zas = defaultArg o.zas ""
+ let foo = match o.foo with Some(Some i) -> string i | Some None -> "xx" | None -> "x"
+ bar + zas + foo
+
+ {| bar = Some 3; zas = Some "ooooo"; foo = Some None |} |> add |> equal "3oooooxx"
+ {| bar = Some 22; zas = Some ""; foo = Some(Some 999) |} |> add |> equal "22999"
+ {| bar = None; zas = None; foo = None |} |> add |> equal "-x"
+ {| foo = Some None; bar = None; zas = None |} |> add |> equal "-xx"
+
+[]
+let ``test Anonymous records can have optional function fields`` () =
+ let add (o: {| bar: (int -> int -> int) option; foo: int -> int -> int |}) =
+ let fn = o.bar
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ o.foo 3 4 + f1 + f2
+
+ {| bar = Some (+); foo = (*) |} |> add |> equal 36
+ {| bar = None; foo = (+) |} |> add |> equal -1
+
[]
let ``test SRTP works with anonymous records`` () =
let ar = [| {|Id=Id"foo"; Name="Sarah"|}; {|Id=Id"bar"; Name="James"|} |]
diff --git a/tests/Python/TestType.fs b/tests/Python/TestType.fs
index 25a23a487c..32522edccb 100644
--- a/tests/Python/TestType.fs
+++ b/tests/Python/TestType.fs
@@ -1,6 +1,8 @@
module Fable.Tests.TypeTests
open System
+open System.Runtime.InteropServices
+open Fable.Core
open Util.Testing
// Check if custom attributes can be created
@@ -575,6 +577,166 @@ type IndexedProps(v: int) =
type ITesting =
static member Testing x = x
+[]
+type MyOptionalClass(?arg1: float, ?arg2: string, ?arg3: int) =
+ member val P1 = defaultArg arg1 1.0
+ member val P2 = defaultArg arg2 "1"
+ member val P3 = defaultArg arg3 1
+
+type VeryOptionalInterface =
+ abstract Bar: int option
+ abstract Baz: bool option
+ abstract Bax: float option
+ abstract Wrapped: unit option
+ abstract Foo: string option with get, set
+ abstract Fn: (int -> int -> int) option
+ abstract Fn2: (int -> int -> int)
+
+type VeryOptionalClass () =
+ let mutable foo = "ab"
+ interface VeryOptionalInterface with
+ member _.Bar = Some 3
+ member _.Baz = None
+ member _.Wrapped = Some ()
+ member _.Bax =
+ let mutable h = ["6"] |> List.tryHead
+ h |> Option.map float
+ member _.Foo
+ with get() = Some foo
+ and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+ member _.Fn = Some (+)
+ member _.Fn2 = (*)
+
+type StaticClass =
+ []
+ static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
+ []
+ static member NamedParams(?name : string, ?age: int) =
+ let name = defaultArg name "John"
+ let age = defaultArg age 30
+ $"%s{name} is %d{age} years old"
+ static member FSharpOptionalParam(?value: bool) = defaultArg value true
+ static member FSharpOptionalParam2(?value: unit) = Option.isSome value
+ static member DefaultParam([] value: bool) = value
+ static member DefaultParam2([] value: Nullable) = if value.HasValue then value.Value + 2 else 3
+ static member DefaultNullParam([] x: obj) = x
+ static member inline InlineAdd(x: int, ?y: int) = x + (defaultArg y 2)
+
+[]
+let ``test Optional arguments work`` () =
+ let x = MyOptionalClass(?arg2 = Some "2")
+ (x.P1, x.P2, x.P3) |> equal (1.0, "2", 1)
+ let y = MyOptionalClass(2.0)
+ (y.P1, y.P2, y.P3) |> equal (2.0, "1", 1)
+ let z = MyOptionalClass(?arg3 = Some 2)
+ (z.P1, z.P2, z.P3) |> equal (1.0, "1", 2)
+
+[]
+let ``test Can implement interface optional properties`` () =
+ let veryOptionalValue = VeryOptionalClass() :> VeryOptionalInterface
+ veryOptionalValue.Bar |> equal (Some 3)
+ veryOptionalValue.Baz |> Option.isSome |> equal false
+ veryOptionalValue.Wrapped |> Option.isSome |> equal true
+ veryOptionalValue.Bax |> equal (Some 6.)
+ veryOptionalValue.Foo <- Some "z"
+ veryOptionalValue.Foo |> equal (Some "abz")
+ veryOptionalValue.Foo <- None
+ veryOptionalValue.Foo |> equal (Some "abz")
+ let fn = veryOptionalValue.Fn
+ let fn2 = veryOptionalValue.Fn2
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ f1 + f2 - fn2 9 3 |> equal -3
+
+[]
+let ``test Can implement interface optional properties with object expression`` () =
+ let veryOptionalValue =
+ let mutable foo = "ab"
+ { new VeryOptionalInterface with
+ member _.Bar = Some 3
+ member _.Baz = None
+ member _.Wrapped = Some ()
+ member _.Bax = ["6"] |> List.tryHead |> Option.map float
+ member _.Foo
+ with get() = Some foo
+ and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+ member _.Fn = Some (+)
+ member _.Fn2 = (*)
+ }
+
+ veryOptionalValue.Bar |> equal (Some 3)
+ veryOptionalValue.Baz |> Option.isSome |> equal false
+ veryOptionalValue.Wrapped |> Option.isSome |> equal true
+ veryOptionalValue.Bax |> equal (Some 6.)
+ veryOptionalValue.Foo <- Some "z"
+ veryOptionalValue.Foo |> equal (Some "abz")
+ veryOptionalValue.Foo <- None
+ veryOptionalValue.Foo |> equal (Some "abz")
+ let fn = veryOptionalValue.Fn
+ let fn2 = veryOptionalValue.Fn2
+ let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+ let f2 = match fn with Some f -> f 1 8 | None -> -5
+ f1 + f2 - fn2 9 3 |> equal -3
+
+[]
+let ``test Optional named params work`` () =
+ StaticClass.NamedParams(foo="5", bar=4) |> equal 9
+ StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
+ StaticClass.NamedParams(foo="14") |> equal 11
+ StaticClass.NamedParams() |> equal "John is 30 years old"
+
+[]
+let ``test F# optional param works`` () =
+ let mutable f1 = fun (v: bool) ->
+ StaticClass.FSharpOptionalParam(value=v)
+ let mutable f2 = fun (v: bool option) ->
+ StaticClass.FSharpOptionalParam(?value=v)
+ StaticClass.FSharpOptionalParam() |> equal true
+ StaticClass.FSharpOptionalParam(true) |> equal true
+ StaticClass.FSharpOptionalParam(false) |> equal false
+ StaticClass.FSharpOptionalParam(?value=None) |> equal true
+ StaticClass.FSharpOptionalParam(?value=Some true) |> equal true
+ StaticClass.FSharpOptionalParam(?value=Some false) |> equal false
+ f1 true |> equal true
+ f1 false |> equal false
+ None |> f2 |> equal true
+ Some false |> f2 |> equal false
+ Some true |> f2 |> equal true
+
+[]
+let ``test F# optional param works with no-wrappable options`` () =
+ let mutable f1 = fun (v: unit) ->
+ StaticClass.FSharpOptionalParam2(value=v)
+ let mutable f2 = fun (v: unit option) ->
+ StaticClass.FSharpOptionalParam2(?value=v)
+ StaticClass.FSharpOptionalParam2() |> equal false
+ StaticClass.FSharpOptionalParam2(()) |> equal true
+ StaticClass.FSharpOptionalParam2(?value=None) |> equal false
+ StaticClass.FSharpOptionalParam2(?value=Some ()) |> equal true
+ f1 () |> equal true
+ None |> f2 |> equal false
+ Some () |> f2 |> equal true
+
+[]
+let ``test DefaultParameterValue works`` () =
+ StaticClass.DefaultParam() |> equal true
+ StaticClass.DefaultParam(true) |> equal true
+ StaticClass.DefaultParam(false) |> equal false
+
+ StaticClass.DefaultParam2(5) |> equal 7
+ StaticClass.DefaultParam2(Nullable()) |> equal 3
+ StaticClass.DefaultParam2() |> equal 3
+
+[]
+let ``test DefaultParameterValue works with null`` () = // See #3326
+ StaticClass.DefaultNullParam() |> isNull |> equal true
+ StaticClass.DefaultNullParam(5) |> isNull |> equal false
+
+[]
+let ``test Inlined methods can have optional arguments`` () =
+ StaticClass.InlineAdd(2, 3) |> equal 5
+ StaticClass.InlineAdd(5) |> equal 7
+
// TODO: This test produces different results in Fable and .NET
// See Fable.Transforms.FSharp2Fable.TypeHelpers.makeTypeGenArgs
// []
diff --git a/tests/Rust/tests/src/MiscTests.fs b/tests/Rust/tests/src/MiscTests.fs
index dc76f22f3a..43ffc3dd3e 100644
--- a/tests/Rust/tests/src/MiscTests.fs
+++ b/tests/Rust/tests/src/MiscTests.fs
@@ -466,12 +466,6 @@ type Shape =
| Square of int
| Rectangle of int * int
-type StaticClass =
- // static member DefaultParam([] value: bool) = value
- // static member DefaultNullParam([] x: obj) = x
- static member inline Add(x: int, ?y: int) =
- x + (defaultArg y 2)
-
type ValueType =
struct
val public X : int
@@ -1323,20 +1317,6 @@ let ``While with isNone doesn't hang with Some ()`` () =
let ``Removing optional arguments not in tail position works`` () =
Internal.MyType.Add(y=6) |> equal 26
-[]
-let ``Inlined methods can have optional arguments`` () =
- StaticClass.Add(2, 3) |> equal 5
- StaticClass.Add(5) |> equal 7
-
-// []
-// let ``DefaultParameterValue works`` () =
-// StaticClass.DefaultParam() |> equal true
-
-// []
-// let ``DefaultParameterValue works with null`` () =
-// StaticClass.DefaultNullParam() |> isNull |> equal true
-// StaticClass.DefaultNullParam(5) |> isNull |> equal false
-
[]
let ``Ignore shouldn't return value`` () = // See #1360
let producer () = 7
diff --git a/tests/Rust/tests/src/RecordTests.fs b/tests/Rust/tests/src/RecordTests.fs
index e10f247fd3..75c7e3aeba 100644
--- a/tests/Rust/tests/src/RecordTests.fs
+++ b/tests/Rust/tests/src/RecordTests.fs
@@ -267,12 +267,6 @@ type Car = { Interior: CarInterior }
// $"Tell me {r.Y} {r.F r.X 3} times"
// |> equal "Tell me Foo 8 times"
-// []
-// let ``SRTP works with anonymous records`` () =
-// let ar = [| {|Id=Id"foo"; Name="Sarah"|}; {|Id=Id"bar"; Name="James"|} |]
-// replaceById {|Id=Id"ja"; Name="Voll"|} ar |> Seq.head |> fun x -> equal "Sarah" x.Name
-// replaceById {|Id=Id"foo"; Name="Anna"|} ar |> Seq.head |> fun x -> equal "Anna" x.Name
-
type Time =
static member inline duration(value: {| from: int; until: int |}) = value.until - value.from
static member inline duration(value: {| from: int |}) = Time.duration {| value with until = 10 |}
@@ -285,6 +279,36 @@ let ``Anonymous records work`` () =
// x = {| y with Baz = 23 |} |> equal true // Doesn't compile
x = {| y with Bar = 14 |} |> equal false
+// []
+// let ``Anonymous records can have optional fields`` () =
+// let add (o: {| bar: int option; zas: string option; foo: int option option |}) =
+// let bar = o.bar |> Option.map string |> Option.defaultValue "-"
+// let zas = defaultArg o.zas ""
+// let foo = match o.foo with Some(Some i) -> string i | Some None -> "xx" | None -> "x"
+// bar + zas + foo
+
+// {| bar = Some 3; zas = Some "ooooo"; foo = Some None |} |> add |> equal "3oooooxx"
+// {| bar = Some 22; zas = Some ""; foo = Some(Some 999) |} |> add |> equal "22999"
+// {| bar = None; zas = None; foo = None |} |> add |> equal "-x"
+// {| foo = Some None; bar = None; zas = None |} |> add |> equal "-xx"
+
+// []
+// let ``Anonymous records can have optional function fields`` () =
+// let add (o: {| bar: (int -> int -> int) option; foo: int -> int -> int |}) =
+// let fn = o.bar
+// let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+// let f2 = match fn with Some f -> f 1 8 | None -> -5
+// o.foo 3 4 + f1 + f2
+
+// {| bar = Some (+); foo = (*) |} |> add |> equal 36
+// {| bar = None; foo = (+) |} |> add |> equal -1
+
+// []
+// let ``SRTP works with anonymous records`` () =
+// let ar = [| {|Id=Id"foo"; Name="Sarah"|}; {|Id=Id"bar"; Name="James"|} |]
+// replaceById {|Id=Id"ja"; Name="Voll"|} ar |> Seq.head |> fun x -> equal "Sarah" x.Name
+// replaceById {|Id=Id"foo"; Name="Anna"|} ar |> Seq.head |> fun x -> equal "Anna" x.Name
+
[]
let ``Overloads with anonymous record arguments don't have same mangled name`` () =
Time.duration {| from = 1 |} |> equal 9
diff --git a/tests/Rust/tests/src/TypeTests.fs b/tests/Rust/tests/src/TypeTests.fs
index 124ec5dff7..70deb555ff 100644
--- a/tests/Rust/tests/src/TypeTests.fs
+++ b/tests/Rust/tests/src/TypeTests.fs
@@ -1,5 +1,7 @@
module Fable.Tests.TypeTests
+// open System.Runtime.InteropServices
+open Fable.Core
open Util.Testing
// Check if custom attributes can be created
@@ -499,6 +501,166 @@ type IndexedProps(v: int) =
// type ITesting =
// static member Testing x = x
+[]
+type MyOptionalClass(?arg1: float, ?arg2: string, ?arg3: int) =
+ member val P1 = defaultArg arg1 1.0
+ member val P2 = defaultArg arg2 "1"
+ member val P3 = defaultArg arg3 1
+
+// type VeryOptionalInterface =
+// abstract Bar: int option
+// abstract Baz: bool option
+// abstract Bax: float option
+// abstract Wrapped: unit option
+// abstract Foo: string option with get, set
+// abstract Fn: (int -> int -> int) option
+// abstract Fn2: (int -> int -> int)
+
+// type VeryOptionalClass () =
+// let mutable foo = "ab"
+// interface VeryOptionalInterface with
+// member _.Bar = Some 3
+// member _.Baz = None
+// member _.Wrapped = Some ()
+// member _.Bax =
+// let mutable h = ["6"] |> List.tryHead
+// h |> Option.map float
+// member _.Foo
+// with get() = Some foo
+// and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+// member _.Fn = Some (+)
+// member _.Fn2 = (*)
+
+type StaticClass =
+// []
+// static member NamedParams(foo: string, ?bar: int) = int foo + (defaultArg bar -3)
+// []
+// static member NamedParams(?name : string, ?age: int) =
+// let name = defaultArg name "John"
+// let age = defaultArg age 30
+// $"%s{name} is %d{age} years old"
+// static member FSharpOptionalParam(?value: bool) = defaultArg value true
+// static member FSharpOptionalParam2(?value: unit) = Option.isSome value
+// static member DefaultParam([] value: bool) = value
+// static member DefaultParam2([] value: Nullable) = if value.HasValue then value.Value + 2 else 3
+// static member DefaultNullParam([] x: obj) = x
+ static member inline InlineAdd(x: int, ?y: int) = x + (defaultArg y 2)
+
+[]
+let ``Optional arguments work`` () =
+ let x = MyOptionalClass(?arg2 = Some "2")
+ (x.P1, x.P2, x.P3) |> equal (1.0, "2", 1)
+ let y = MyOptionalClass(2.0)
+ (y.P1, y.P2, y.P3) |> equal (2.0, "1", 1)
+ let z = MyOptionalClass(?arg3 = Some 2)
+ (z.P1, z.P2, z.P3) |> equal (1.0, "1", 2)
+
+// []
+// let ``Can implement interface optional properties`` () =
+// let veryOptionalValue = VeryOptionalClass() :> VeryOptionalInterface
+// veryOptionalValue.Bar |> equal (Some 3)
+// veryOptionalValue.Baz |> Option.isSome |> equal false
+// veryOptionalValue.Wrapped |> Option.isSome |> equal true
+// veryOptionalValue.Bax |> equal (Some 6.)
+// veryOptionalValue.Foo <- Some "z"
+// veryOptionalValue.Foo |> equal (Some "abz")
+// veryOptionalValue.Foo <- None
+// veryOptionalValue.Foo |> equal (Some "abz")
+// let fn = veryOptionalValue.Fn
+// let fn2 = veryOptionalValue.Fn2
+// let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+// let f2 = match fn with Some f -> f 1 8 | None -> -5
+// f1 + f2 - fn2 9 3 |> equal -3
+
+// []
+// let ``Can implement interface optional properties with object expression`` () =
+// let veryOptionalValue =
+// let mutable foo = "ab"
+// { new VeryOptionalInterface with
+// member _.Bar = Some 3
+// member _.Baz = None
+// member _.Wrapped = Some ()
+// member _.Bax = ["6"] |> List.tryHead |> Option.map float
+// member _.Foo
+// with get() = Some foo
+// and set(v) = foo <- match v with Some v -> foo + v | None -> foo
+// member _.Fn = Some (+)
+// member _.Fn2 = (*)
+// }
+
+// veryOptionalValue.Bar |> equal (Some 3)
+// veryOptionalValue.Baz |> Option.isSome |> equal false
+// veryOptionalValue.Wrapped |> Option.isSome |> equal true
+// veryOptionalValue.Bax |> equal (Some 6.)
+// veryOptionalValue.Foo <- Some "z"
+// veryOptionalValue.Foo |> equal (Some "abz")
+// veryOptionalValue.Foo <- None
+// veryOptionalValue.Foo |> equal (Some "abz")
+// let fn = veryOptionalValue.Fn
+// let fn2 = veryOptionalValue.Fn2
+// let f1 = fn |> Option.map (fun f -> f 6 9) |> Option.defaultValue -3
+// let f2 = match fn with Some f -> f 1 8 | None -> -5
+// f1 + f2 - fn2 9 3 |> equal -3
+
+// []
+// let ``Optional named params work`` () =
+// StaticClass.NamedParams(foo="5", bar=4) |> equal 9
+// StaticClass.NamedParams(foo="3", ?bar=Some 4) |> equal 7
+// StaticClass.NamedParams(foo="14") |> equal 11
+// StaticClass.NamedParams() |> equal "John is 30 years old"
+
+// []
+// let ``F# optional param works`` () =
+// let mutable f1 = fun (v: bool) ->
+// StaticClass.FSharpOptionalParam(value=v)
+// let mutable f2 = fun (v: bool option) ->
+// StaticClass.FSharpOptionalParam(?value=v)
+// StaticClass.FSharpOptionalParam() |> equal true
+// StaticClass.FSharpOptionalParam(true) |> equal true
+// StaticClass.FSharpOptionalParam(false) |> equal false
+// StaticClass.FSharpOptionalParam(?value=None) |> equal true
+// StaticClass.FSharpOptionalParam(?value=Some true) |> equal true
+// StaticClass.FSharpOptionalParam(?value=Some false) |> equal false
+// f1 true |> equal true
+// f1 false |> equal false
+// None |> f2 |> equal true
+// Some false |> f2 |> equal false
+// Some true |> f2 |> equal true
+
+// []
+// let ``F# optional param works with no-wrappable options`` () =
+// let mutable f1 = fun (v: unit) ->
+// StaticClass.FSharpOptionalParam2(value=v)
+// let mutable f2 = fun (v: unit option) ->
+// StaticClass.FSharpOptionalParam2(?value=v)
+// StaticClass.FSharpOptionalParam2() |> equal false
+// StaticClass.FSharpOptionalParam2(()) |> equal true
+// StaticClass.FSharpOptionalParam2(?value=None) |> equal false
+// StaticClass.FSharpOptionalParam2(?value=Some ()) |> equal true
+// f1 () |> equal true
+// None |> f2 |> equal false
+// Some () |> f2 |> equal true
+
+// []
+// let ``DefaultParameterValue works`` () =
+// StaticClass.DefaultParam() |> equal true
+// StaticClass.DefaultParam(true) |> equal true
+// StaticClass.DefaultParam(false) |> equal false
+
+// StaticClass.DefaultParam2(5) |> equal 7
+// StaticClass.DefaultParam2(Nullable()) |> equal 3
+// StaticClass.DefaultParam2() |> equal 3
+
+// []
+// let ``DefaultParameterValue works with null`` () = // See #3326
+// StaticClass.DefaultNullParam() |> isNull |> equal true
+// StaticClass.DefaultNullParam(5) |> isNull |> equal false
+
+[]
+let ``Inlined methods can have optional arguments`` () =
+ StaticClass.InlineAdd(2, 3) |> equal 5
+ StaticClass.InlineAdd(5) |> equal 7
+
// // TODO: This test produces different results in Fable and .NET
// // See Fable.Transforms.FSharp2Fable.TypeHelpers.makeTypeGenArgs
// // []