Important coding style guidelines for React and Typescript developers

react typescript coding styleguides

Coding styles are subjective. Every developer, whether junior or senior, has a certain preferred way of writing software. Some prefer tabs vs some prefer spaces. To bring uniformity, readability and ease of comprehension to a codebase, its often recommended to have a common style guides which are followed and enforced by everyone for everyone across all contributions to the codebase.

Source

I recently started leading a large size team to rewrite our tightly coupled, infinitely entangled UI codebase. Few days into this project, we had a long running PR review which was stuck on the discussion of whether to use arrow based functions or simple functions, whether to use class to wrap all function and declare all functions as static etc. I had to step in and make a call to stop this "review-lock".

I immediately decided to standardize coding style guides for this new project. We were primarily writing react components with typescript. I finalized Google TS styleguide and Airbnb react styleguide for our project.

Source

In this article, I am going to capture the top 5-10 style guide recommendations from above two guides.

Typescript

  • All abbreviations / acronyms should be treated as whole words, when writing variable names. E.g instead of writing loadHTTPURL , you should write loadHttpUrl
  • Do not use _ (underscore) as a prefix / suffix in any identifiers ( variables, class names, function names etc. )
  • Do not use var. Use const or let instead. By default mark all variables as const. const and let are block scoped, like variables in most other languages. var in JavaScript is function scoped, which can cause difficult to understand bugs. Don't use it.
const foo = otherValue; // Use if "foo" never changes.
let bar = someValue; // Use if "bar" is ever assigned into later on.
  • Iterating objects with for(... in ...) is error prone. It will include enumerable properties from the prototype chain. Either filter values explicitly with an if statement, or use for (... of Object.keys(...)).
for (const x in someObj) {
  if (!someObj.hasOwnProperty(x)) continue;
  // now x was definitely defined on someObj
}
for (const x of Object.keys(someObj)) {
  // note: for _of_!
  // now x was definitely defined on someObj
}
for (const [key, value] of Object.entries(someObj)) {
  // note: for _of_!
  // now key was definitely defined on someObj
}
  • Do not use for (... in ...) to iterate over arrays. It will counterintuitively give the array's indices (as strings!), not values:
for (const x in someArray) {
  // x is the index!
}

Prefer for (... of someArr) to iterate over arrays.

for (let i = 0; i < someArr.length; i++) {
  // Explicitly count if the index is needed, otherwise use the for/of form.
  const x = someArr[i];
  // ...
}
for (const [i, x] of someArr.entries()) {
  // Alternative version of the above.
}
  • It is not recommended to include properties initialized to arrow functions in classes because it can create confusion about the meaning of "this" and make call sites and references appear broken, as these functions require the calling function to know that the callee's "this" is already bound. In other words, it requires non-local knowledge to determine whether such handlers are correct.

  • To ensure that a project can be moved around without needing changes in imports, code should use relative imports (./foo) when referring to files within the same logical project, instead of absolute import paths (path/to/foo).

  • Do not use default exports. This ensures that all imports follow a uniform pattern. Why? Default exports provide no canonical name, which makes central maintenance difficult with relatively little benefit to code owners, including potentially decreased readability:

  • Do not create container classes with static methods or properties for the sake of namespacing.

export class Container {
  static FOO = 1;
  static bar() {
    return 1;
  }
}

Instead, export individual constants and functions:

export const FOO = 1;
export function bar() {
  return 1;
}
  • Arrange packages based on features, rather than their type. To illustrate, for an online shop, the packages should be named as products, checkout, backend, instead of views, models, and controllers.

  • TypeScript allows type aliases to assign a name to a type expression which can include primitive, union, tuple and various other types. However, it is recommended to use interfaces instead of type aliases when declaring types for objects.

  • The use of 'any' can be hazardous as it has the potential to hide serious programming mistakes, and using it diminishes the benefits of having static types in the code.

React / JSX

Source

  • Always use camelCase for prop names.
// bad
<Foo
  UserName="hello"
  phone_number={12345678}
/>

// good
<Foo
  userName="hello"
  phoneNumber={12345678}
/>
  • Omit the value of the prop when it is explicitly true.
// bad
<Foo
  hidden={true}
/>

// good
<Foo
  hidden
/>

// good
<Foo hidden />
  • Always include an alt prop on tags. If the image is presentational, alt can be an empty string or the must have role="presentation".
// bad
<img src="hello.jpg" />

// good
<img src="hello.jpg" alt="Me waving hello" />

// good
<img src="hello.jpg" alt="" />

// good
<img src="hello.jpg" role="presentation" />
  • Always define explicit defaultProps for all non-required props. Why? propTypes are a form of documentation, and providing defaultProps means the reader of your code doesn’t have to assume as much. In addition, it can mean that your code can omit certain type checks.
// bad
function SFC({ foo, bar, children }) {
  return (
    <div>
      {foo}
      {bar}
      {children}
    </div>
  );
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node
};

// good
function SFC({ foo, bar, children }) {
  return (
    <div>
      {foo}
      {bar}
      {children}
    </div>
  );
}
SFC.propTypes = {
  foo: PropTypes.number.isRequired,
  bar: PropTypes.string,
  children: PropTypes.node
};
SFC.defaultProps = {
  bar: '',
  children: null
};
  • Use spread props sparingly. Why? Otherwise you’re more likely to pass unnecessary props down to components. And for React v15.6.1 and older, you could pass invalid HTML attributes to the DOM.

  • Do not use underscore prefix for internal methods of a React component. Why? Underscore prefixes are sometimes used as a convention in other languages to denote privacy. But, unlike those languages, there is no native support for privacy in JavaScript, everything is public. Regardless of your intentions, adding underscore prefixes to your properties does not actually make them private, and any property (underscore-prefixed or not) should be treated as being public.

// bad
React.createClass({
  _onClickSubmit() {
    // do stuff
  },

  // other stuff
});

// good
class extends React.Component {
  onClickSubmit() {
    // do stuff
  }

  // other stuff
}

These are just some of the guidelines which I felt capturing. Definitely go through the original guides for more guidelines.