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.
- Tooling and Enforcement
- Language Features
- Type-Safe JavaScript with JSDoc
- Code Organization
- Naming Conventions
- Security Best Practices
Consistent tooling is key to maintaining quality in a JavaScript codebase.
- 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-prettieris 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,
};- Use
constby default. Useletonly for variables that must be reassigned. Avoidvar. - Always use arrow functions for anonymous functions and when you need to preserve the
thiscontext. - 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.
async/await is the required standard for all asynchronous operations. It provides a clearer, more linear control flow than Promise chains.
- Always wrap
awaitcalls in atry/catchblock to handle errors. - Avoid mixing
async/awaitwith.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;
}
}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-checkto 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;
}
}- 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).
- Variables and Functions:
camelCase - Classes and Components:
PascalCase - Constants:
UPPER_SNAKE_CASE - Booleans: Should be prefixed with
is,has, orshould(e.g.,isVisble,hasPermission). - File Names:
kebab-case(e.g.,user-profile.js).
- Sanitize User Input: Never trust user input. Use libraries like
DOMPurifyto 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.