Docs / BuckleScript / HandlingJsNamingCollisions

Handling JS Naming Collisions

JavaScript has different naming conventions and has only very few limitations when it comes to naming variables, classes, functions, JS object attributes etc.

Reason on the contrary has more restrictive naming rules which need to be considered whenever you define a type, function, value, module, record attribute or similar. Here are a few typical naming restrictions which cause trouble with JS:

  • Every name needs to start with a lowercase letter (uppercase is reserved for module names)

  • For the same reason, names can't be all caps (very common for JS constants: const MY_CONSTANT = "my_constant")

  • It's not possible to use reserved keywords for names

  • Labeled arguments (for defining JSX attributes) can't be named after keywords and can't start with an uppercase letter

  • etc.

Of course, when doing interop, we still want to be able to map to the JS equivalent (preferably without any runtime overhead). In this section we describe some common scenarios on how to gracefully handle naming collisions.

Using reserved keywords as JSX props

Many React components have a prop named type in JavaScript:

JS
/* this won't work in Reason since `type` is a reserved keyword! */ <Component type="title" />

If you're using a React component with a reserved keyword as a prop name, then simply prepend a underscore (so that it's a valid Reason name):

RE
/* This works because `_type` is not a reserved keyword */ <Component _type="title" />

The Reason compiler will remove the leading underscore when outputting JavaScript (so the JavaScript will have <Component type="POST" />).

The removal of the _ is called "Name mangling". The ruleset for this behavior is discussed further down below.

Accessing JavaScript object attributes that start with a capital letter

Capital letters in Reason are used exclusively for module names, like String and Belt, and they cannot be used as record field names like in JavaScript.

JS
const payload = { PostTitle: "Welcome to Reason", }; /* this won't work in Reason since `PostTitle` is capitalized, so `paylod.PostTitle` would break */ const title = payload.PostTitle;

In this case, when writing bindings to the JavaScript object, you can use the [@bs.as "whatever-name-you-want-in-javascript"] to tell the compiler exactly what the JavaScript attribute name should be in the compiled output:

RE
type payload { [@bs.as "PostTitle"] postTitle: string } let payload = { postTitle: "Welcome to Reason" } /* Reason is happy since we're using the valid `postTitle` field name */ let title = payload.postTitle;

The code above will be translated to following JS:

JS
/* The correct capitalized field name is output in the JavaScript! */ var title = payload.PostTitle;

Accessing reserved keywords as JavaScript object attribute names

Just like accessing attributes that start with a capital letter, we can use [@bs.as "the-reserved-keyword-that-javascript-wants"]. It's customary to append an underscore (unlike the JSX case, where we prepend the underscore) to the reserved keyword name:

RE
type payload { [@bs.as "type"] type_: string } let payload = { type_: "Documentation" } /* Reason is happy since we're using the valid `type_` field name */ let payloadType = payload.type_;

The code above will be translated to following JS:

JS
/* The reason compiler has correctly ouput `payload.type` even though *we* called the field `type_` */ var payloadType = payload.type;

Special name mangling rules for JS object attribute names

Note: This is special behavior partly implemented in the Reason syntax, partly in the BuckleScript compiler. This section is mostly useful for understanding how JS object attributes and labeled arguments of ReasonReact components are compiled.

Another Note: A JS object type is a structurally typed entity with special compilation behavior, so they act differently than records or plain Reason objects. They are encoded as Js.t({...}) types, more details about that feature can be found here.

Labeled arguments used in [@react.component] functions (like let make = (~name: string, ~age: int) => React.element) are transformed into the Js.t representation (e.g. let make = Js.t({."name": string, "age": int}) => React.element), so they follow the same ruleset.

When accessing a JavaScript object field in a structural way (e.g. myJsObject##some), the following rules apply:

  1. A single leading underscore will be dropped from the output: myJsObject##_type => myJsObject.type

  2. Two (or more) leading underscores will be kept in the output: myJsObject##__type => myJsObject.__type

  3. There is no way to access e.g. myJsObject##_type structurally - use records and [@bs.as "_type"] instead

  4. The final trailing double underscores (and anything following them) will be dropped from the output: myJsObject##this_is_kept__this_is_omitted => myJsObject.this_is_kept