https://github.com/aeberdinelli/js-guidelines
Documentation about how I believe code should look like, good practice and tips.
https://github.com/aeberdinelli/js-guidelines
codestyle es6 eslint javascript node
Last synced: 8 months ago
JSON representation
Documentation about how I believe code should look like, good practice and tips.
- Host: GitHub
- URL: https://github.com/aeberdinelli/js-guidelines
- Owner: aeberdinelli
- Created: 2021-04-29T05:00:16.000Z (almost 5 years ago)
- Default Branch: master
- Last Pushed: 2022-12-03T19:03:24.000Z (about 3 years ago)
- Last Synced: 2025-04-10T18:06:35.315Z (10 months ago)
- Topics: codestyle, es6, eslint, javascript, node
- Homepage:
- Size: 81.1 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
This is my set of a few good practice guidelines for JS/TS. In my experience, applying these rules will make the code a lot easier to maintain.
Table of contents
{
* [General rules](#-general-rules)
* [General code style rules](#-general-style-rules)
* [Always prefer const](#-always-prefer-const)
* [Updating an array](#updating-an-array)
* [Updating an object](#updating-an-object)
* [Exports](#-exports)
* [Export only what you need](#export-only-what-you-need)
* [Export named methods](#export-named-methods)
* [Arrow functions](#-arrow-functions)
* [Null checks](#-null-checks)
* [Defaulting a value](#defaulting-a-value)
* [Optional property operator](#using-the-new-optional-property-operator)
* [Looping through arrays](#-looping-through-arrays)
* [Removing items based on condition](#removing-items-based-on-condition)
* [Performing operation for each element](#performing-an-operation-for-each-element)
* [Customizing elements](#customizing-each-element-of-the-array)
* [Using certain items from array](#using-a-certain-item-from-array)
* [Spread operator](#-spread-operator)
* [Conditions](#-conditions)
* [Do not use redundant checks](#do-not-use-redundant-checks)
* [Redundant ternary operators](#this-applies-to-ternary-operators-as-well)
* [Redundant function returns](#and-in-functions-too)
* [Redundant else](#you-dont-need-else)
* [Typescript](#-typescript)
* [Type everything!](#type-everything)
* [Dynamic types](#dynamic-types)
* [Keep it simple](#keep-it-simple)
* [Avoid duplicated types](#avoid-duplicated-types)
* [Monorepos?](#monorepo)
}
# Guidelines for JS {
## 🠹 General rules
This applies to any language not just JS!
- **Avoid using libraries** when possible, especially if you can replicate the methods easily (lodash users I'm talking to you). Reasons? Over-sizing your app, and adding code you don't know.
- **Always focus on readability** rather than "looking smart". Avoid using shorthands or hacks, or using single-character named variables. At the end of the day, the language is not what will make your app slow. If you need to optimize your app, try to optimize images and http requests.
- **Prevent duplicated code**. You can create any number of reusable functions and it will be far better than re-writing code. Why? If you have to refactor later, you will have to find every copy of the logic while you could just update the function instead.
- **Keep functions small**. Your function should not work for more than 1 use case. Avoid using a lot of if/else that changes the way it works, create several functions instead. The only place where you could change the implementation based on conditions is within your controllers. In that case, you would call the correct function based on your desired condition.
## 🠹 General style rules
- Always use `;` at the end of each line
- Always include spaces between brackets: `import { Thing, OtherThing } from './stuff';`
- Always declare arrays in single line when it has only a few elements: `[ 'thing', 'thing2' ];`
- Keep your code easy to understand, if you're using ternary operators, use it only for one condition - **do not chain them**
- No matter what kind of indentation you use (spaces/tabs) as long as you use width of 4, this keeps the code clean and very easy to read
- Use spaces after each parameter in a function or method and between different parts of the declaration: `function something(param1, param2, param3) {`
- Use spaces to keep conditions and loops easy to read: `if (condition) {` / `for (let i = 0; i < 5; i++) {`
## 🠹 Always prefer const
I prefer this for easy debugging and preventing errors. Remember, `const` means you cannot re-assign that variable, but you can change it's value if it is an array or an object.
Updating an array
```javascript
const rows = [];
// This is a React example, but it's valid for other cases too
people.forEach(person => {
rows.push(
{person.name}
);
});
```
Updating an object
```javascript
const settings = {};
settings.darkMode = true;
```
## 🠹 Exports
Export only what you need
If you have a module with the main method being something like `lowercase`, export only that method.
```javascript
function internal(str) {
return str.toLowerCase();
}
function lowercase(str) {
return internal(str);
}
// Do not export everything
module.exports = { internal, lowercase };
// Instead, export the useful methods
module.exports = { lowercase };
```
Export named methods
This also applies to classes. Is better to export a named method instead of a default export. This way, you will force everyone to use your method with the name that you specified and will make maintaince a lot easier, knowing exactly each place the exported method is used. Let me show you an example:
```javascript
// This is a default export
module.exports = function() {}
// This is a named export
function something() {}
module.exports = { something };
// Example for default export usage
// Both will work and they will have different name while they are the same method
const something = require('./something');
const notSomething = require('./something');
// This will make the method use the same name across your project (unless changing it on purpose)
// Plus instead of importing the whole module, you just import the method you need
const { something } = require('./something');
```
## 🠹 Arrow functions?
I know, arrow functions are sexy. However, they are not always needed. Considerate using them only when:
- You need to maintain context for `this`
- You only have a single-line logic, like when using `.map()` or `.filter()`
This will make also the previous rule easier to keep.
## 🠹 Null checks
Always add nullchecks to prevent errors in your console and breaking the flow.
You can use the falsy check or defaulting some value with the `||` operator. For example:
Defaulting a value
```javascript
const name = user.name || 'Unavailable';
```
Using the new optional property operator
```javascript
const name = user?.details?.name || 'Unavailable';
```
🠹 Looping through arrays
Try to not use the same iterator method everytime. Instead, use whichever is the better for your case.
Removing items based on condition
```javascript
const adults = people.filter(person => person.age >= 18);
```
Performing an operation for each element
Use `.forEach()` if you don't need to create a new array
```javascript
people.forEach(person => registerPerson(person));
```
Customizing each element of the array
In this case, you can use `.map()` since you need a new array with elements changed
```javascript
// This way you add new properties to array
const people = peopleArray.map(person => ({
...person,
isAdult: (person.age >= 18)
});
```
Using a certain item from array
If you know what items you have in an array and want to use them, for instance the result of a `.split()` operation, you can do something like this:
```javascript
// Instead of
const parts = fullname.split(' ');
const name = parts[0];
const lastname = parts[1];
// You can do
const [ name, lastname ] = fullname.split(' ');
```
🠹 Spread operator
I know sometimes it looks fancy but if you need to grab 8 properties from a 10 properties object, then it's more convenient to just use the object instead of spreading each property from it:
```jsx
// Instead of doing
const { prop1, prop2, prop3, prop4, prop5, prop6, prop7, prop8, prop9, prop10, prop11, prop12 } = obj;
// Refence the object itself
Name: {obj.prop1}
Lastname: {obj.prop2}
```
Your code will look cleaner with a lot less lines while getting the same result.
🠹 Conditions
### Do not use redundant checks
If you have a condition that always returns either `true` or `false`, you don't need to compare it:
```javascript
// Do not use
if (something == true) {
// Instead just do
if (something) {
```
### This applies to ternary operators as well
```javascript
// Do not use
result = (condition != "") ? true : false;
// Just do this
result = (condition != "");
```
### And in functions too
```javascript
// Do not do this
function isSomethingTrue(something) {
if (something) {
return true;
} else {
return false;
}
}
// Instead do this
function isSomethingTrue(something) {
return (something);
}
```
### You don't need `else`
Sounds weird, but trust me your code will start to look a lot cleaner and easier to understand.
You can use `return` or `throw` to stop the flow.
There's a good article [right here ↗️](https://medium.com/@jamousgeorges/dont-use-else-66ee163deac3) if you'd like to read more about this.
```javascript
function getName() {
if (!person.name) {
return 'Name not available';
}
return person.name;
}
```
🠹 Typescript
"The whole point of using JS is that I don't have to care about types". I know, most JS developers when first presented TS hate it for this reason. I can assure you, it will help you prevent a lot of common errors. There's no need to use it but if you do, here's some useful tips.
### Type everything!!
Types are not only useful to prevent errors and fail compilation if there's a typo somewhere. They're also extremely useful for new developers. You can define the interface of your API (and other things) and welcome new developers to your project.
```typescript
interface CreateUserRequest {
name: string;
lastname: string;
age?: number;
}
// This way people just need to check CreateUserRequest interface to find out what properties you accept
// Also if the IDE supports it, they will get autocomplete to make the DX delightful.
export function CreateUser(body: CreateUserRequest) {
```
### Keep it simple
Let's say your app depends on a service that returns a huge object and you only need a few properties. You don't need to type every property in your project, instead, you can use an interface with the properties that you know and use.
### Avoid duplicated types
You don't need to rewrite types if you only have to discard one of two properties. Imagine you have a REST API and you have a `product` entity. When you create an object for that entity, you don't know the Id, but when you make a GET request you do. In that case you can do something like this:
```typescript
export type Product {
name: string;
price: number;
id: string;
}
// This is the same as Product, without the id
export type ProductCreate = Omit;
```
### Dynamic types
Most TS developers don't take full advantage of the features available. There are a lot of places where `any` should not be an option. Usually they use `any` when they want to reuse a method across different entities. But you don't need to do that!
```typescript
// ResponseType is dynamic and you can send TS what type you are expecting from the method
export function Request(userId: number): Response {
return { name: 'Alan' } as ResponseType;
}
// You use it like this:
type User = { name: string };
// response will be of type User and the IDE will get full autocomplete options too
const response = Request(1);
```
### Monorepo?
If you have a React/Node app and a monorepo, you can share your types between both parts of the app. This way, you only have to update the types in one place and is easier to maintain.
# };