name
JavaScript Coding Rules
version
1.0.0
author
Mathias Paulenko Echeverz
description
Coding standards and conventions for JavaScript and TypeScript projects. Covers ESLint-configured rules, async/await patterns, module structure, and modern JS/TS idioms.
tags
javascript
typescript
coding-rules
style-guide
eslint
async-await
role
coding-standard
type
rules
language
en
Use TypeScript for all new projects.
Use ES2022+ features (optional chaining, nullish coalescing, top-level await where appropriate).
Target ES2020 for transpiled output (broad compatibility).
Use const by default.
Use let only when reassignment is necessary.
Never use var.
// Good
const user = await fetchUser ( id ) ;
let attempts = 0 ;
attempts ++ ;
// Bad
var user = await fetchUser ( id ) ;
Prefer async/await over .then()/.catch() chains.
Use Promise.all() for independent parallel operations.
Handle errors with try/catch or .catch() at call site.
// Good
async function loadDashboard ( ) : Promise < DashboardData > {
const [ user , orders , notifications ] = await Promise . all ( [
fetchUser ( ) ,
fetchOrders ( ) ,
fetchNotifications ( ) ,
] ) ;
return { user, orders, notifications } ;
}
// Bad
function loadDashboard ( ) {
return fetchUser ( ) . then ( user =>
fetchOrders ( ) . then ( orders =>
fetchNotifications ( ) . then ( notifications =>
( { user, orders, notifications } )
)
)
) ;
}
Use interface for object shapes that may be extended.
Use type for unions, tuples, and complex mapped types.
Use unknown instead of any for values of uncertain type.
Use never for exhaustive checks.
// Good
interface User {
id : string ;
email : string ;
role : 'admin' | 'editor' | 'viewer' ;
}
type ApiResponse < T > = { data : T } | { error : ApiError } ;
function process ( value : unknown ) : string {
if ( typeof value === 'string' ) return value ;
return JSON . stringify ( value ) ;
}
5. Equality and Comparison
Always use strict equality (===, !==).
Use nullish coalescing (??) for default values.
Use optional chaining (?.) for nested property access.
// Good
if ( user . role === 'admin' ) { ... }
const name = user . name ?? 'Anonymous' ;
const street = user . address ?. street ;
// Bad
if ( user . role == 'admin' ) { ... }
const name = user . name || 'Anonymous' ; // Wrong for empty string
Use arrow functions for callbacks and closures.
Use named functions for top-level exports (better stack traces).
Prefer single-responsibility functions.
Use destructuring for parameters.
// Good
export function formatUser ( { name, email, createdAt } : User ) : string {
return `${ name } <${ email } > (joined ${ createdAt . toDateString ( ) } )` ;
}
const activeUsers = users . filter ( user => user . isActive ) ;
// Bad
function formatUser ( user ) {
return user . name + ' <' + user . email + '>' ;
}
Use ES modules (import/export).
Avoid default exports for libraries; use named exports.
Group imports: external → internal → types.
Use import type for type-only imports.
// Good
import React from 'react' ;
import type { ReactNode } from 'react' ;
import { UserCard } from '@/components/UserCard' ;
import type { User } from '@/types' ;
// Bad
const React = require ( 'react' ) ;
Create custom Error subclasses for domain errors.
Always handle Promise rejections.
Use Result<T, E> pattern for synchronous operations that may fail.
// Good
class ValidationError extends Error {
constructor ( public fields : Record < string , string > ) {
super ( 'Validation failed' ) ;
}
}
try {
await submitForm ( data ) ;
} catch ( error ) {
if ( error instanceof ValidationError ) {
showFieldErrors ( error . fields ) ;
} else {
showGenericError ( ) ;
console . error ( error ) ;
}
}
9. React / Component Rules
Use functional components with hooks.
Use React.FC sparingly; prefer explicit props interfaces.
Extract logic into custom hooks.
Memoize expensive computations with useMemo.
Memoize callbacks with useCallback when passed to optimized children.
Use key prop correctly in lists.
// Good
interface UserListProps {
users : User [ ] ;
onSelect : ( id : string ) => void ;
}
export function UserList ( { users, onSelect } : UserListProps ) : JSX . Element {
const sortedUsers = useMemo ( ( ) =>
[ ...users ] . sort ( ( a , b ) => a . name . localeCompare ( b . name ) ) ,
[ users ]
) ;
return (
< ul >
{ sortedUsers. map ( user => (
< li key = { user . id } >
< button onClick = { ( ) => onSelect ( user . id ) } > { user . name } < / b u t t o n >
< / li >
) ) }
< / u l >
) ;
}
Element
Convention
Example
Files
kebab-case.ts
user-service.ts
Components
PascalCase.tsx
UserProfile.tsx
Functions
camelCase
fetchUserById
Constants
UPPER_SNAKE_CASE
MAX_RETRY_COUNT
Interfaces
PascalCase
UserRepository
Types
PascalCase
ApiResponse
Enums
PascalCase, members UPPER_SNAKE_CASE
Status.ACTIVE
Boolean vars
is/has/should prefix
isLoading, hasPermission
11. Linting and Formatting
Use ESLint + Prettier + TypeScript :
// .eslintrc.json
{
"extends" : [
" eslint:recommended" ,
" plugin:@typescript-eslint/recommended" ,
" plugin:react-hooks/recommended" ,
" prettier"
],
"rules" : {
"@typescript-eslint/no-explicit-any" : " error" ,
"@typescript-eslint/explicit-function-return-type" : " warn" ,
"no-console" : [" warn" , { "allow" : [" error" ] }]
}
}
// .prettierrc
{
"semi" : true ,
"singleQuote" : true ,
"tabWidth" : 2 ,
"trailingComma" : " es5" ,
"printWidth" : 100
}
Use Vitest or Jest for unit tests.
Use React Testing Library for component tests.
Use Playwright for E2E tests.
Use MSW (Mock Service Worker) for API mocking.
// Good
import { render , screen } from '@testing-library/react' ;
import userEvent from '@testing-library/user-event' ;
import { UserCard } from './UserCard' ;
test ( 'displays user name and triggers click' , async ( ) => {
const handleSelect = vi . fn ( ) ;
render ( < UserCard user = { mockUser } onSelect = { handleSelect } / > ) ;
expect ( screen . getByText ( mockUser . name ) ) . toBeInTheDocument ( ) ;
await userEvent . click ( screen . getByRole ( 'button' ) ) ;
expect ( handleSelect ) . toHaveBeenCalledWith ( mockUser . id ) ;
} ) ;