Skip to content

Latest commit

 

History

History
172 lines (140 loc) · 5.7 KB

File metadata and controls

172 lines (140 loc) · 5.7 KB

JavaScript Coding Conventions

A Note on TypeScript

For all new projects at Bayat, TypeScript is the required standard. It provides static type checking, modern language features, and improved tooling, which leads to more robust and maintainable code.

This JavaScript guide should be considered for the following scenarios only:

  • Maintaining existing legacy JavaScript projects.
  • Writing small, standalone scripts where a build step is undesirable.
  • Prototyping where speed is prioritized over long-term maintainability.

When working in a JavaScript codebase, following these conventions is crucial.

Table of Contents

Tooling and Enforcement

Consistent tooling is key to maintaining quality in a JavaScript codebase.

ESLint and Prettier

  • ESLint is used for static analysis to find problems.
  • Prettier is used for all code formatting.
  • These tools should be configured to work together. eslint-config-prettier is used to disable ESLint rules that conflict with Prettier.

Example .eslintrc.js configuration:

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'prettier', // Turns off all rules that are unnecessary or might conflict with Prettier
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  rules: {
    'no-var': 'error',
    'prefer-const': 'error',
    'eqeqeq': ['error', 'always'],
    'no-console': ['warn', { allow: ['warn', 'error'] }],
  },
};

Example .prettierrc.js configuration:

module.exports = {
  semi: true,
  trailingComma: 'es5',
  singleQuote: true,
  printWidth: 80,
  tabWidth: 2,
};

Language Features

Modern Syntax (ES6+)

  • Use const by default. Use let only for variables that must be reassigned. Avoid var.
  • Always use arrow functions for anonymous functions and when you need to preserve the this context.
  • Use template literals for string interpolation.
  • Use destructuring for objects and arrays to improve readability.
  • Use the spread operator (...) for array and object cloning and manipulation.
  • Use optional chaining (?.) and nullish coalescing (??) to safely handle potentially null or undefined values.

Asynchronous Code

async/await is the required standard for all asynchronous operations. It provides a clearer, more linear control flow than Promise chains.

  • Always wrap await calls in a try/catch block to handle errors.
  • Avoid mixing async/await with .then() and .catch().
// GOOD: Clear, linear, and robust error handling
async function fetchUserData(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Failed to fetch user data:', error);
    // Re-throw or handle the error appropriately
    throw error;
  }
}

Type-Safe JavaScript with JSDoc

For projects that must remain in JavaScript, you can still get many of the benefits of type checking by using JSDoc annotations. When combined with the @ts-check directive, VS Code and the TypeScript compiler can analyze your code and find type-related errors.

  • Add // @ts-check to the top of your JavaScript files to enable type checking.
  • Use JSDoc syntax to define types for functions, parameters, and objects.

Example of JSDoc for Type Checking:

// @ts-check

/**
 * @typedef {object} User
 * @property {string} id
 * @property {string} name
 * @property {string} email
 */

/**
 * Fetches a user by their ID.
 * @param {string} userId - The ID of the user to fetch.
 * @returns {Promise<User|null>} A promise that resolves with the user object or null if not found.
 */
async function getUserById(userId) {
  try {
    const response = await fetch(`/api/users/${userId}`);
    if (response.status === 404) {
      return null;
    }
    /** @type {User} */
    const user = await response.json();
    return user;
  } catch (error) {
    console.error('Error fetching user:', error);
    throw error;
  }
}

Code Organization

  • Single Responsibility Principle: Functions and modules should do one thing well.
  • Keep functions small: Aim for functions under 30 lines.
  • Use ES Modules (import/export): This is the standard for all modern JavaScript. Avoid CommonJS (require).

Naming Conventions

  • Variables and Functions: camelCase
  • Classes and Components: PascalCase
  • Constants: UPPER_SNAKE_CASE
  • Booleans: Should be prefixed with is, has, or should (e.g., isVisble, hasPermission).
  • File Names: kebab-case (e.g., user-profile.js).

Security Best Practices

  • Sanitize User Input: Never trust user input. Use libraries like DOMPurify to prevent XSS attacks when inserting content into the DOM.
  • Server-Side Validation: Always validate data on the server, even if you have client-side validation.
  • For more detailed guidelines, refer to the Secure Coding Standards.