Docs / BuckleScript / CommonDataTypes

Common Data Types

Shared Data Types

BuckleScript's primitives such as string, float, array and a few others have a rather interesting property: they compile to the exact same thing in JavaScript! Thanks to this, there's close to no API to learn for these data types.

This means that if you receive e.g. a string from the JS side, you can use it without conversion on the BS side, and vice-versa. In other words, string and others are "guaranteed public representations".

P.S. if you need an overview of the language primitives themselves, see the Reason docs.

BuckleScript uses the same standard library as OCaml; see the docs here (in Reason syntax). Additionally, we provide access to all the familiar JS primitives here. You can mix and match these two.


Immutable on both sides, as expected. BuckleScript String API. JS String API.

Unicode Support

OCaml string is an immutable byte sequence. If the user types some unicode:


It'll compile to the following JS:


Which gives you cryptic console output. To rectify this, BuckleScript exposes a special js annotation to the default quoted string syntax built into the language. Use it like this:

Js.log({js|你好, 世界|js})

This'll correctly output:



For convenience, we also expose another special tag quoted string annotation, j, which supports the equivalent of JS' string interpolation:

let world = {j|世界|j}; let helloWorld = {j|你好,$world|j};

You can surround the variable in parentheses too: {j|你好,$(world)|j}.


BuckleScript floats are JS numbers, vice-versa. The OCaml standard library doesn't come with a Float module. JS Float API is here.


Ints are 32-bits! Be careful, you can potentially treat them as JS numbers and vice-versa, but if the number's large, then you better treat JS numbers as floats. For example, we bind to Js.Date using floats. Js Int API here.


Idiomatic OCaml arrays are supposed to be fix-sized. This constraint is relaxed on the BuckleScript size. You can change its length using the usual JS Array API. BuckleScript's own Array API is here.

Empty arrays

Sometimes, the compiler may raise an error when you have an empty array (let a = [||];). This is because an empty array is "weakly typed." The compiler doesn't know what type it will contain until you add a value to it. Without the error, Js.Array.push could unsafely add incompatible values to the same array. When this error happens, you can correct it by explicitly annotating the type of the array: let a: array(int) = [||];.


OCaml tuples are compiled to JS arrays. Convenient when you're interop-ing with a JS array that contains heterogeneous values, but happens to have a fixed length. Model it as a tuple on the BS side!


Since BuckleScript 3, OCaml/Reason bool now compile to JS boolean.


Since BuckleScript v7, OCaml/Reason records map directly to JS objects. If records contain any Non-Shared data types (like variants), then these values must be transformed separately and cannot be directly used in JS.

Non-shared Data Types

Variants (including option and list), BuckleScript objects, lazy values and others can be exported as well, but you should not rely on their internal representation on the JS side. Aka, don't grab a BS list and start manipulating its structure on the JS side.

However, for BuckleScript related data types, we provide generation of converters and accessors. Once you convert e.g. variants to a string, you can naturally use them on the JS side.

For list, use Array.of_list and Array.to_list in the Array module. option will be highlighted shortly later on and also has its dedicated section as well.

For a seamless JS / TypeScript / Flow integration experience, you might want to use genType instead of doing convertion by hand.

Design Decisions

As to why we don't compile list to JS array or vice-versa, it's because OCaml array and JS array share similar characteristics: mutable, similar read/write performance, etc. List, on the other hand, is immutable and has different access perf characteristics.

The same justification applies for records. OCaml records are fixed, nominally typed, and in general doesn't work well with JS objects. We do provide excellent facilities to bind to JS objects in the object section.

Cheat Sheet


OCaml/BS/Reason TypeJavaScript Type
tuplearray. (3, 4) -> [3, 4]
optionNone -> undefined
optionSome( Some .. Some (None)) -> internal representation
optionSome other -> other
recordobject. {x: 1; y: 2} -> {x: 1, y: 2}
special bs.deriving abstract recordobject


Again, the representations are subject to change.

OCaml/BS/Reason TypeJavaScript Value
int64array. [high, low]. high is signed, low unsigned
char'a' -> 97
bytesnumber array (we might encode it as buffer in NodeJS)
list[] -> 0, [x, y] -> [x, [y, 0]], [1, 2, 3] -> [ 1, [ 2, [ 3, 0 ] ] ]
Polymorphic variant**
lazy value-

* Variants with a single non-nullary constructor:

type tree = Leaf | Node(int, tree, tree); /* Leaf -> 0 */ /* Node a b c -> [a, b, c] */

Variants with more than one non-nullary constructor:

type u = A(string) | B(int); /* A(a) -> [a].tag = 0 -- tag 0 assignment is optional */ /* B(b) -> [b].tag = 1 */

** Polymorphic Variant:

`a /* 97 */ `a(1, 2) /* [ 97, [1, 2] ] */

BuckleScript compiles unit to 0 in some cases but don't worry about it because it's considered internal and subject to change.