2016-07-04 18:15:15 +02:00
Matrix JavaScript/ECMAScript Style Guide
========================================
The intention of this guide is to make Matrix's JavaScript codebase clean,
consistent with other popular JavaScript styles and consistent with the rest of
the Matrix codebase. For reference, the Matrix Python style guide can be found
2020-05-30 01:42:23 +02:00
at https://github.com/matrix-org/synapse/blob/master/docs/code_style.md
2016-07-04 18:15:15 +02:00
2016-07-07 13:09:02 +02:00
This document reflects how we would like Matrix JavaScript code to look, with
acknowledgement that a significant amount of code is written to older
standards.
Write applications in modern ECMAScript and use a transpiler where necessary to
target older platforms. When writing library code, consider carefully whether
to write in ES5 to allow all JavaScript application to use the code directly or
writing in modern ECMAScript and using a transpile step to generate the file
that applications can then include. There are significant benefits in being
able to use modern ECMAScript, although the tooling for doing so can be awkward
for library code, especially with regard to translating source maps and line
2020-05-30 01:43:55 +02:00
number through from the original code to the final application.
2016-07-07 13:09:02 +02:00
2016-07-04 18:15:15 +02:00
General Style
-------------
- 4 spaces to indent, for consistency with Matrix Python.
2019-11-20 11:30:34 +01:00
- 120 columns per line, but try to keep JavaScript code around the 90 column mark.
2016-07-11 17:27:17 +02:00
Inline JSX in particular can be nicer with more columns per line.
2016-07-04 18:15:15 +02:00
- No trailing whitespace at end of lines.
- Don't indent empty lines.
- One newline at the end of the file.
- Unix newlines, never `\r`
- Indent similar to our python code: break up long lines at logical boundaries,
more than one argument on a line is OK
- Use semicolons, for consistency with node.
- UpperCamelCase for class and type names
- lowerCamelCase for functions and variables.
- Single line ternary operators are fine.
2019-10-06 13:08:46 +02:00
- UPPER_SNAKE_CASE for constants
2016-11-01 12:11:37 +01:00
- Use parentheses or `` ` `` instead of ` \` for line continuation where ever possible
2016-07-04 18:23:38 +02:00
- Open braces on the same line (consistent with Node):
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2016-07-04 18:15:15 +02:00
if (x) {
console.log("I am a fish"); // Good
}
if (x)
{
console.log("I am a fish"); // Bad
}
2016-07-04 18:23:38 +02:00
```
- Spaces after `if` , `for` , `else` etc, no space around the condition:
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2016-07-04 18:15:15 +02:00
if (x) {
console.log("I am a fish"); // Good
}
if(x) {
console.log("I am a fish"); // Bad
}
if ( x ) {
console.log("I am a fish"); // Bad
}
2016-07-04 18:23:38 +02:00
```
2017-05-19 00:03:34 +02:00
- No new line before else, catch, finally, etc:
```javascript
if (x) {
console.log("I am a fish");
} else {
console.log("I am a chimp"); // Good
}
if (x) {
console.log("I am a fish");
}
else {
console.log("I am a chimp"); // Bad
}
```
2016-07-04 18:15:15 +02:00
- Declare one variable per var statement (consistent with Node). Unless they
are simple and closely related. If you put the next declaration on a new line,
2016-07-04 18:23:38 +02:00
treat yourself to another `var` :
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2017-05-18 23:51:22 +02:00
const key = "foo",
2016-07-04 18:15:15 +02:00
comparator = function(x, y) {
return x - y;
}; // Bad
2017-05-18 23:51:22 +02:00
const key = "foo";
const comparator = function(x, y) {
2016-07-04 18:15:15 +02:00
return x - y;
}; // Good
2017-05-18 23:51:22 +02:00
let x = 0, y = 0; // Fine
2016-07-04 18:15:15 +02:00
2017-05-18 23:51:22 +02:00
let x = 0;
let y = 0; // Also fine
2016-07-04 18:23:38 +02:00
```
- A single line `if` is fine, all others have braces. This prevents errors when adding to the code.:
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2016-07-04 18:15:15 +02:00
if (x) return true; // Fine
if (x) {
return true; // Also fine
}
if (x)
return true; // Not fine
2016-07-04 18:23:38 +02:00
```
2016-07-08 16:42:42 +02:00
- Terminate all multi-line lists, object literals, imports and ideally function calls with commas (if using a transpiler). Note that trailing function commas require explicit configuration in babel at time of writing:
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2016-07-04 18:15:15 +02:00
var mascots = [
"Patrick",
"Shirley",
"Colin",
"Susan",
"Sir Arthur David" // Bad
];
var mascots = [
"Patrick",
"Shirley",
"Colin",
"Susan",
"Sir Arthur David", // Good
];
2016-07-04 18:23:38 +02:00
```
2016-07-04 18:15:15 +02:00
- Use `null` , `undefined` etc consistently with node:
Boolean variables and functions should always be either true or false. Don't set it to 0 unless it's supposed to be a number.
When something is intentionally missing or removed, set it to null.
2016-07-07 13:09:02 +02:00
If returning a boolean, type coerce:
2016-07-11 17:21:21 +02:00
2016-07-11 17:28:41 +02:00
```javascript
2016-07-07 13:09:02 +02:00
function hasThings() {
return !!length; // bad
return new Boolean(length); // REALLY bad
return Boolean(length); // good
}
```
2016-07-04 18:15:15 +02:00
Don't set things to undefined. Reserve that value to mean "not yet set to anything."
Boolean objects are verboten.
- Use JSDoc
2020-05-20 17:01:34 +02:00
- Use switch-case statements where there are 5 or more branches running against the same variable.
2016-07-04 18:15:15 +02:00
ECMAScript
----------
2016-07-11 11:10:51 +02:00
- Use `const` unless you need a re-assignable variable. This ensures things you don't want to be re-assigned can't be.
2016-07-04 18:15:15 +02:00
- Be careful migrating files to newer syntax.
- Don't mix `require` and `import` in the same file. Either stick to the old style or change them all.
- Likewise, don't mix things like class properties and `MyClass.prototype.MY_CONSTANT = 42;`
- Be careful mixing arrow functions and regular functions, eg. if one function in a promise chain is an
arrow function, they probably all should be.
- Apart from that, newer ES features should be used whenever the author deems them to be appropriate.
2021-01-19 09:32:52 +01:00
TypeScript
----------
- TypeScript is preferred over the use of JavaScript
2021-01-26 09:23:11 +01:00
- It's desirable to convert existing JavaScript files to TypeScript. TypeScript conversions should be done in small
2021-01-19 09:32:52 +01:00
chunks without functional changes to ease the review process.
- Use full type definitions for function parameters and return values.
- Avoid `any` types and `any` casts
2016-07-04 18:15:15 +02:00
React
-----
2016-07-07 13:09:02 +02:00
- Pull out functions in props to the class, generally as specific event handlers:
2016-07-11 17:21:21 +02:00
2016-07-08 16:59:34 +02:00
```jsx
2016-07-07 13:09:02 +02:00
< Foo onClick = {function(ev) { doStuff ( ) ; } } > // Bad
< Foo onClick = {(ev) = > {doStuff();}}> // Equally bad
< Foo onClick = {this.doStuff} > // Better
2016-07-08 16:58:18 +02:00
< Foo onClick = {this.onFooClick} > // Best, if onFooClick would do anything other than directly calling doStuff
2016-07-07 13:09:02 +02:00
```
2018-12-06 23:18:46 +01:00
- Prefer classes that extend `React.Component` (or `React.PureComponent` ) instead of `React.createClass`
- You can avoid the need to bind handler functions by using [property initializers ](https://reactjs.org/docs/react-component.html#constructor ):
```js
class Widget extends React.Component
onFooClick = () => {
...
}
}
```
- To define `propTypes` , use a static property:
```js
class Widget extends React.Component
static propTypes = {
...
}
}
```
- If you need to specify initial component state, [assign it ](https://reactjs.org/docs/react-component.html#constructor ) to `this.state` in the constructor:
```js
constructor(props) {
super(props);
// Don't call this.setState() here!
this.state = { counter: 0 };
}
```
2021-01-19 09:32:52 +01:00
- Prefer class components over function components and hooks (not a strict rule though)
2016-07-07 13:09:02 +02:00
- Think about whether your component really needs state: are you duplicating
information in component state that could be derived from the model?
2019-12-08 13:16:17 +01:00
- Avoid things marked as Legacy or Deprecated in React 16 (e.g string refs and legacy contexts)