Skip to content

Latest commit

 

History

History
437 lines (326 loc) · 9.66 KB

File metadata and controls

437 lines (326 loc) · 9.66 KB

Javascript

The following includes all guidelines specific for Javascript.

Quotes

  • Use single quotes instead of double quotes.
// bad
const message = 'This is not cool.';

// good
const message = 'This is super cool.';
  • Use string literals only when the string contains a dynamic value.
// bad
const message = `This is not cool.`;

// good
const message = `Order with number ${orderNumber} got created.`;

Semicolons

  • Use semicolons.
// bad
const order = getOrder(..)

// bad
{
  return order
}

// good
const order = getOrder(...);

// good
{
  return order;
}

Function expressions vs. function declarations

  • Prefer functions expressions to function declarations.
// bad
function createOrder(): IOrder {
  // ...
}

// good
const createOrder = (): IOrder => {
  // ...
};

Destructuring

  • Use object destructuring when accessing and using multiple properties of an object.
  • Only applies for first-level objects where there aren't nullable or undefined keys. This would lead to setting unnecessary defaults
// bad
const getFullName = (user: IUser): string => {
  const firstName = user.firstName;
  const lastName = user.lastName;

  return `${firstName} ${lastName}`;
};

// good
const getFullName = (user: IUser): string => {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
};

// best
const getFullName = ({ firstName, lastName }: IUser): string => `${firstName} ${lastName}`;

eslint: prefer-destructuring.

Optional chaining

  • Use when trying to access nested object properties that can be nullable or undefined.
const adventurer = {
  name: 'Alice',
  cat: {
    name: 'Dinah',
  },
};

const dogName = adventurer.dog?.name;

This feature will only be available in packages/services migrated to Babel 7 and above. Enable strictNullChecks will automatically recommend this practice.

Parameters

The maximum number of function parameters should be 3, and you should always try to use 2 or less.

  • If you need more than 3 parameter values, use an object.
// bad
const createOrder = (
  param1: param1Type,
  param2: param2Type,
  param3: param3Type,
  param4: param4Type,
): ReturnType => {
  // ...
};

// good
const createOrder = (param1: param1Type, param2: param2Type): ReturnType => {
  // ...
};

// good
const createOrder = ({ param1, param2, param3, param4 }: ObjectType): ReturnType => {
  // ...
};
  • Always put default parameters last.
// bad
const handleThings = (opts: optsType = {}, name: nameType) => {
  // ...
};

// good
const handleThings = (name: nameType, opts: optsType = {}) => {
  // ...
};

Promises

Async/await

  • Prefer using async/await whenever possible instead of regular chainable promises or callbacks.
// bad
const createOrder = async (cb: cbType): Promise<ReturnType> => {
  isAdminUser(() => {
    cb(...)
  })
}

// bad
const createOrder = async (): Promise<ReturnType> {
  return isAdminUser(...)
   .then((isAdmin) => {
      // ...
   })
}

// good
const createOrder = async (): Promise<ReturnType> => {
  const isAdmin = await isAdminUser(...)
  // ...
}
  • Do not use async when await is not present.
// bad
const getName = async (): Promise<string> => {
  // ...
  return "Core API team"
}

// good
const getName = async (): Promise<ReturnType> {
  // ...
  const isAdmin = await isAdminUser(...)
  return ...
}

eslint: require-await

Conditional cases

If case

  • Always use curly brackets
  • Avoid else case when returning within all available if cases
// bad
{
  if (merchantFeeRate <= standardFeeRate) return merchantFeeRate;
  if (discountedFeeRate < standardFeeRate) {
    return standardFeeRate;
  } else {
    return discountedFeeRat;
  }
}

// good
{
  if (merchantFeeRate <= standardFeeRate) {
    return merchantFeeRate;
  }

  if (discountedFeeRate < standardFeeRate) {
    return standardFeeRate;
  }

  return discountedFeeRat;
}

Ternary if

  • Use ternary operators to set a value to a variable, or to reduce code if necessary.
  • Use if-else statements for everything else.
// bad
const createdOrder = (await createOrder(values))
  ? await createExternalOrder(otherValues)
  : x.name === 'Core'
  ? await createCoreOrder(otherValues)
  : null;

// good
const host = url ? new URL(url).host : '';

Nullish coalescing operator

  • Logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined, and otherwise returns its left-hand side operand.
  • The problem with && and || operators is the definition of truthy and falsy leads to bugs. These operators work well with null or undefined values, but other falsy values can produce unexpected results.
  • The nullish coalescing operator ?? acts very similar to the operator ||, except that it doesn’t use “truthy” when evaluating the operator. Instead, it uses the definition of “nullish”, which means that the value is strictly equal to null or undefined.
const team = null ?? 'A team'; // A team
const falsy = 0 ?? 'zero'; // 0

Variables

Const vs let vs var

  • Do not use var
// bad
var name = 'Core API team';

// good
const name = 'Core API team';
  • Use const instead of let.
// bad
let name = 'Core API team';

// good
const name = 'Core API team';
  • Create a separate function if let is reassigning value.
// bad
const createOrder = (): ReturnType => {
  // ...
  let name;
  if(orderNumber === 1) {
    // ...
    const merchant = await getMerchant(...)
    name = "special order"
  } else {
    // ...
    const merchant = await getSpecialMerchant(...)
    name = "normal order"
  }
}

// good
function getOrderName = (orderNumber: number): ReturnType => {
  if(orderNumber === 1) {
    // ...
    return "special order"
  }

  // ...
  return "normal order"
}

function createOrder() {
  // ...
  const name = getOrderName(orderNumber)
  // ...
}

Creating variables

const summary = getReportSummary(invoice);
const transactions = getReportTransactions(invoice);

return { summary, transactions };

// or

return {
  summary: getReportSummary(invoice),
  transactions: getReportTransactions(invoice),
};
  • Don't create variable right before returning if it has no extra value or isn't being used anywhere.
// bad
const ms = (hours, minutes, seconds): number => {
  const duration = ((hours * 60 + minutes) * 60 + seconds) * 1000;
  return duration;
};

// good
const ms = (hours, minutes, seconds) => ((hours * 60 + minutes) * 60 + seconds) * 1000;

eslint: sonarjs prefer-immediate-return

Functions

Length

  • A function should be maximum 20 lines long.
  • Use warning for these since it could happen occasionally.
  • Blank lines and comments should be skipped.

eslint: max-lines-per-function

Complexity

  • Avoid complex function where the flow becomes impossible to read.
  • Functions with high Cognitive Complexity will be difficult to maintain.

eslint: sonarjs cognitive-complexity, eslint: complexity.

Newlines

  • Add newline at the end of file

eslint: eol-last

  • Add newline between declarations if it improves readability.

eslint: padding-line-between-statements

  • Add newline between test suites
describe('Create order', () => {
  // ...
});

describe('Update order', () => {
  // ...
});
  • Add newline between other test cases/specs
describe('Create order', () => {
  it('Should create order with all requirements', () => {
    // ...
  });

  it('Should throw error when customer order is missing', () => {
    // ...
  });
});

Hardcoded values

  • Do not use hardcoded values for connection strings etc.
// bad
const mongoUrl = 'localhost:4000';

// good
const mongUrl = process.env.MONGO_URL;

Classes

  • Classes are a good fit for encapsulating stateful systems, such as the Kafka client.
  • Do not use classes by default, you should be able to explain why you chose to use them!
  • Generally, we use the default ESlint guidelines for writing classes. This includes, among other conventions:
    • Underscoring private functions with an underscore _

References