Skip to content

Commit 673043a

Browse files
committed
Add use-illegal-identifier-names for v8.0.0
1 parent eb904e3 commit 673043a

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
---
2+
title: "Use Illegal Identifier Names"
3+
description: "Handling (JS) naming collisions in ReScript"
4+
canonical: "/docs/manual/latest/use-illegal-identifier-names"
5+
---
6+
7+
# Use Illegal Identifier Names
8+
9+
<Warn>
10+
11+
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.
12+
13+
</Warn>
14+
15+
JavaScript has different naming conventions and has only very few limitations when it comes to naming variables, classes, functions, JS object attributes etc.
16+
17+
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:
18+
- Every name needs to start with a lowercase letter (uppercase is reserved for module names)
19+
- For the same reason, names can't be all caps (very common for JS constants: `const MY_CONSTANT = "my_constant"`)
20+
- It's not possible to use [reserved keywords](/docs/manual/latest/reserved-keywords) for names
21+
- Labeled arguments (for defining JSX attributes) can't be named after keywords and can't start with an uppercase letter
22+
- etc.
23+
24+
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.
25+
26+
## Using reserved keywords as JSX props
27+
28+
Many React components have a prop named `type` in JavaScript:
29+
30+
```js
31+
/* this won't work in Reason since `type` is a reserved keyword! */
32+
<Component type="title" />
33+
```
34+
35+
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):
36+
37+
```reason
38+
/* This works because `_type` is not a reserved keyword */
39+
<Component _type="title" />
40+
```
41+
42+
The Reason compiler will remove the leading underscore when outputting JavaScript (so the JavaScript will have `<Component type="POST" />`).
43+
44+
The removal of the `_` is called "Name mangling". The ruleset for this behavior is discussed [further down below](#special-name-mangling-rules-for-js-object-attribute-names).
45+
46+
## Accessing JavaScript object attributes that start with a capital letter
47+
48+
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.
49+
50+
```js
51+
const payload = {
52+
PostTitle: "Welcome to Reason",
53+
};
54+
55+
/* this won't work in Reason since `PostTitle` is capitalized, so `paylod.PostTitle` would break */
56+
const title = payload.PostTitle;
57+
```
58+
59+
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:
60+
61+
```reason
62+
type payload {
63+
[@bs.as "PostTitle"]
64+
postTitle: string
65+
}
66+
67+
let payload = {
68+
postTitle: "Welcome to Reason"
69+
}
70+
71+
/* Reason is happy since we're using the valid `postTitle` field name */
72+
let title = payload.postTitle;
73+
```
74+
75+
The code above will be translated to following JS:
76+
77+
```js
78+
/* The correct capitalized field name is output in the JavaScript! */
79+
var title = payload.PostTitle;
80+
```
81+
82+
## Accessing reserved keywords as JavaScript object attribute names
83+
84+
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:
85+
86+
```reason
87+
type payload {
88+
[@bs.as "type"]
89+
type_: string
90+
}
91+
92+
let payload = {
93+
type_: "Documentation"
94+
}
95+
96+
/* Reason is happy since we're using the valid `type_` field name */
97+
let payloadType = payload.type_;
98+
```
99+
100+
The code above will be translated to following JS:
101+
102+
```js
103+
/* The reason compiler has correctly ouput `payload.type` even though *we* called the field `type_` */
104+
var payloadType = payload.type;
105+
```
106+
107+
## Special name mangling rules for JS object attribute names
108+
109+
> **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.
110+
111+
> **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](http://localhost:3000/docs/reason-compiler/latest/object-2#actual-solution).
112+
>
113+
> 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.
114+
115+
When accessing a JavaScript object field in a structural way (e.g. `myJsObject##some`), the following rules apply:
116+
117+
1. A single _leading_ underscore will be *dropped* from the output: `myJsObject##_type` => `myJsObject.type`
118+
1. Two (or more) _leading_ underscores will be *kept* in the output: `myJsObject##__type` => `myJsObject.__type`
119+
1. There is _no way_ to access e.g. `myJsObject##_type` structurally - use records and `[@bs.as "_type"]` instead
120+
1. 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`

0 commit comments

Comments
 (0)