Supported F# language features and libraries
The compiler follows two rough guidelines when transforming the code:
string and char compile to JS "string" while bool becomes "boolean".
All numeric primitives compile to JS "number" (but see Arrays below).
ResizeArray (alias for System.Generic.Collection.List) and non-numeric
arrays are compiled to native JS arrays. Numeric arrays are compiled to
Typed Arrays
which should provide a performance boost when interacting with HTML5 canvas or WebGL.
If you pass the
--clampargument to the compiler, byte arrays will be compiled asUint8ClampedArray.
The usual string format (with a few limitations) and printing methods in F# and .NET are available:
Console/Debug.WriteLine, String.Format, printfn, sprintf... as well as the
string instance methods.
You can use the Regex class in the same way as .NET, but the regex will always
behave as if passed RegexOptions.ECMAScript flag (e.g., no negative look-behind
or named groups).
You can use DateTime and TimeSpan with the same semantics as in .NET.
TimeSpan will just be the number of milliseconds in JS, and DateTime will
compile down to native JS Date with a kind property attached.
Tuples compile to native arrays. Desestructuring, fst, snd... works normally.
Records are compiled as ES2015 classes
and they can be used with pattern matching (type information is available in runtime).
Record properties will be attached directly to the object instead of the prototype
making them compatible with JSON.parse or any other function accepting plain JS objects.
Unions are also compiled as classes with the case name held in a Case property.
Particular cases: Lists are a bit more optimized (they don't have a tag) and options are erased.
Serialized unions can be read directly with Json.NET
All enumerables compile to ES2015 iterable interface
which means they're fully compatible with compliant JS code and native methods.
The downside is you cannot implement the IEnumerable interface in a data structure,
but you can use seq, array and list comprehensions normally.
All methods in F# Seq module have been implemented. There may be some still missing in List
and Array modules, but in that case the compiler will default to the corresponding
Seq function and build a new list or array from the response if necessary.
Maps and Sets fall back to the ES2015 corresponding classes
for performance. Adding and removing will create new objects. System.Collections.Generic.Dictionary compile to ES2015 Map too
and allows mutable operations. Same for System.Collections.Generic.HashSet.
async computation expressions work as expected. However, RunSynchronously is not available and,
as JS is single-threaded, Start and StartImmediate will have the same effect.
Methods to convert to and from JavaScript Promises
should be simple to do and are planned for the core library.
It's possible to define custom computation expressions normally.
Pattern matching will work normally in JS and it will generate optimized
code to prevent overhead. You can match union types, records, classes or
interfaces (with :? MyClass as x), lists, etc. Destructuring, guards and
multiple targets are also fine.
Active patterns can be used normally.
Generic information disappears in generated code. However, it's accessible
to the compiler, so calls like typeof<MyType> are possible with concrete
types or with generics in inline functions.
Decorators are coming to JavaScript. However, there are competing proposals and it's not yet clear how the definitive specs will be. For now, attributes are only visible to the compiler which uses them, for example, when defining foreign interfaces.
Interface methods are compiled to normal object methods (there's no explicit implementation so names may collide). The interface names will be attached to the type constructor as a Symbol-keyed property, making interface type testing possible at runtime to do patterns like this one proposed by Yan Cui to extend union types.
Overloads are allowed in class implementations (not for interfaces), but they'll
have a suffix attached (_1, _2...) in generated code and are not recommended.
The behaviour is similar for secondary constructors.
Custom operators are possible, just note it won't be idiomatic to call
them from JS if necessary (e.g., Time.op_Addition(ts1, ts2)).
Inheritance is possible and conforms to ES2015 inheritance. Just be careful to use the primary constructor of the base class, not a secondary one. Methods can be overridden too, but you won't be able to access the base methods by casting the object.
Anonymous lambdas will be curried by default. If you want to pass a callback with
more than one argument to JS code, please wrap it in a delegate first (e.g.,
Func<_,_,_>(fun x y -> x + y)). If the signature of the method expects a
delegate, this will be done automatically by the compiler.
Events are not implemented yet, please use Observable instead.
Object expressions are compatible for the most general cases.
Units of measure are compatible (at least for int and float) but they will
be erased from the generated JS code.
You can check the tests when in doubt. If there's a test for something, it's supported :)