API Modelling
One of this projects goals is to provide a consistent and idiomatic API for the Web APIs. The interopt story of ReScript is quite good, but it it has limitations. JavaScript is a dynamic language and has a lot of flexibility. ReScript is a statically typed language and has to model the dynamic parts of JavaScript in a static way.
Dynamic parameters and properties
Some Web APIs have a parameter or property that can be multiple things. In ReScript, you would model this as a variant type. This is an example wrapper around values with a key property to discriminate them.
In JavaScript, this strictness is not enforced and you can pass a string where a number is expected. There are multiple strategies to model this in ReScript and it depends on the specific API which one is the best.
Overloads
One example is addEventListener.
This can access either a boolean or an object as the third parameter.
Because, this is a method, we can model this as an overloaded function in ReScript.
The first two overloads are the same, so we can merge them into one with an optional options parameter.
The third overload takes a boolean and is worth using when you want to change the default of the useCapture
boolean parameter.
We can use a fixed argument to model this.
Decoded variants
We can be pragmatic with overloaded functions and use model them in various creative ways.
For properties, we cannot do this unfortunately. A propery can only be defined once and have a single type.
The strategy here is to use a decoded variant.
Example for the fillStyle property of the CanvasRenderingContext2D
interface can be either a:
string
CanvasGradient
CanvasPattern
These types are not all primitives and thus we cannot define it as untagged variants.
What we can do instead is represent the type as an empty type and use a helper module to interact with this.
When we wish to read and write the fillStyle
property, we can use a helper module to lift the type to an actual ReScript variant:
We can now use FillStyle.decode
to get the actual value of the fillStyle
property.
And use FillStyle.fromString
, FillStyle.fromCanvasGradient
, and FillStyle.fromCanvasPattern
to set the value.
Try and use decoded
and decode
as conventions for the type and function
names.