Docs / Language Manual / Use Illegal Identifier Names
Edit

You are currently looking at the v6.0 - v8.2 docs (Reason v3.6 syntax edition). You can find the latest manual page here.

(These docs are equivalent to the old BuckleScript docs before the ReScript rebrand)

Use Illegal Identifier Names

This page is solely dedicated to Reason v3.6 related naming collisions and also highlights some name mangling rules the ReScript compiler implemented just for Reason purposes.

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):

Reason (Old Syntax)ML (Older Syntax)
/* 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:

Reason (Old Syntax)ML (Older Syntax)JS Output
type payload = {
  [@bs.as "PostTitle"] postTitle: string
};

let payload = {
  postTitle: "Welcome to Reason"
};

/* ReScript is happy since we're using the valid `postTitle` field name */
let 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:

Reason (Old Syntax)ML (Older Syntax)JS Output
type payload = {
  [@bs.as "type"] type_: string
}

let payload = {
  type_: "Documentation"
}

/* ReScript is happy since we're using the valid `type_` field name */
let 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 ReScript 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