JavaScript and TypeScript coding conventions. Use when writing any JS/TS code, creating functions, defining variables, handling async operations, structuring exports, naming files, scaffolding components, or choosing styling approaches. Enforces const-by-default, functional patterns, explicit types, DRY principles, no-abbreviation naming, PascalCase component files, camelCase utilities, folder-per-component structure, co-located tests and styles, styled-components with SCSS (never Tailwind or CSS Modules), shared types in types.ts, PascalCase enums, no barrel imports, and displayName on arrow-function components.
e (event callbacks), i/j (loop indices), req/res/err (Express handlers), props/ref (React)apiUrl, htmlContent, parseXmlDocument// BAD
const usr = getUser();
const evt = new CustomEvent('click');
const btn = document.querySelector('button');
function calcTax(amt: number): number { ... }
function genId(): string { ... }
// GOOD
const user = getUser();
const event = new CustomEvent('click');
const button = document.querySelector('button');
function calculateTax(amount: number): number { ... }
function generateId(): string { ... }
camelCaseALL_CAPS# prefix (ES private) with camelCaseUserProfile.tsx, ApiClient.ts
.component or .class — just the nameauthHelpers.ts, constants.ts, useAuth.ts.tsx/.ts for TypeScript projects; .jsx/.js for JavaScript projects// GOOD file names
UserProfile.tsx // component
ApiClient.ts // class
useAuth.ts // hook (camelCase — not a component)
authHelpers.ts // utility
constants.ts // constants
formatCurrency.ts // utility function
// BAD file names
UserProfile.component.tsx // no .component suffix
user-profile.tsx // not kebab-case
apiClient.ts // class should be PascalCase
class DancePartner {
/** Maximum allowed requests per user per event. */
static readonly MAX_REQUESTS_PER_EVENT = 5;
/** Display name shown in partner discovery. */
displayName: string;
/** Internal request count not exposed to consumers. */
#pendingRequestCount = 0;
/** Validates whether the partner is eligible for the division. */
validateEligibility(division: string): boolean { ... }
/** Recalculates internal match score. */
#recalculateMatchScore(): number { ... }
}
const by default; let only when reassigningvarfunction for named/exported functionsfunction calculatePay(hours: number, rate: number): number {
return hours * rate;
}
items.map((item) => item.value);
{}) for if, else, for, while, and other control flow — no single-line bodies// BAD
if (!user) return;
if (index >= items.length) break;
if (!isValid) throw new Error('Invalid');
for (const item of items) processItem(item);
// GOOD
if (!user) {
return;
}
if (index >= items.length) {
break;
}
if (!isValid) {
throw new Error('Invalid');
}
for (const item of items) {
processItem(item);
}
// Ternaries are fine
const label = isActive ? 'Active' : 'Inactive';
async/await over .then() chainsmap/filter/reduce over imperative loopsexport default ComponentName at endexport function calculateTotal(): number { ... }
export const TAX_RATE = 0.08;
// Component files
function MyComponent() { ... }
export default MyComponent;
Always use a folder-per-component structure. Never place components flat in a shared directory.
components/
UserProfile/
UserProfile.tsx
UserProfile.test.tsx
UserProfile.styles.ts
PaymentForm/
PaymentForm.tsx
PaymentForm.test.tsx
PaymentForm.styles.ts
styles/
shared.ts # shared styled-components live outside component folders
styles/, shared/)pages/Dashboard/Dashboard.tsx)Always add displayName to arrow-function components. Not needed for function declarations — React infers the name automatically.
// Arrow function — displayName REQUIRED
const UserProfile = () => {
return <div>...</div>;
};
UserProfile.displayName = 'UserProfile';
export default UserProfile;
// Function declaration — displayName NOT needed
function UserProfile() {
return <div>...</div>;
}
export default UserProfile;
Never use barrel files (index.ts re-exports). Always import directly from the source file.
Barrels hurt tree-shaking, introduce circular dependency risks, and obscure where code lives.
// BAD — importing through a barrel
import { UserProfile } from '@components';
import { TransactionCore } from '@core/transactions';
// GOOD — importing directly from source
import UserProfile from './components/UserProfile/UserProfile';
import { TransactionCore } from '@core/transactions/TransactionCore';
constants.ts fileutils.ts or dedicated util file// BAD - MyComponent.tsx exports non-component items
export const MAX_ITEMS = 10;
export function formatLabel(s: string): string { ... }
function MyComponent() { ... }
export default MyComponent;
// GOOD - separated concerns
// constants.ts
export const MAX_ITEMS = 10;
// utils.ts
export function formatLabel(s: string): string { ... }
// MyComponent.tsx
import { MAX_ITEMS } from './constants';
import { formatLabel } from './utils';
function MyComponent() { ... }
export default MyComponent;
constants.tsexport const DEFAULT_HOURLY_RATE = 25;
export const MILEAGE_RATE = 0.67;
/**
* Calculates gross pay for an employee based on hours worked.
* Multiplies hours by rate directly without overtime consideration.
* Used by payroll module for standard hourly employees.
*/
export function calculatePay(hours: number, rate: number): number {
return hours * rate;
}
/** Default rate applied when employee has no custom rate assigned. */
export const DEFAULT_HOURLY_RATE = 25;
/**
* Calculates gross pay for an employee based on hours worked.
* Multiplies hours by rate directly without overtime consideration.
* Used by payroll module for standard hourly employees.
* @param {number} hours - Total hours worked in pay period
* @param {number} rate - Hourly rate in dollars
* @returns {number} Gross pay amount
*/
export function calculatePay(hours, rate) {
return hours * rate;
}
/** @type {number} Default rate applied when employee has no custom rate assigned. */
export const DEFAULT_HOURLY_RATE = 25;
.module.css)ComponentName.styles.ts file next to the componentstyles/ directory// UserProfile.styles.ts
import styled from 'styled-components';
export const ProfileContainer = styled.div`
display: flex;
flex-direction: column;
padding: 16px;
&:hover {
background-color: #f5f5f5;
}
.avatar {
border-radius: 50%;
width: 48px;
height: 48px;
}
`;
// UserProfile.tsx
import { ProfileContainer } from './UserProfile.styles';
function UserProfile() {
return (
<ProfileContainer>
<img className="avatar" src={avatarUrl} alt="Profile" />
</ProfileContainer>
);
}
export default UserProfile;
types.ts filetypes.ts (camelCase, like all non-component files)// types.ts — shared across multiple files
export interface UserProfile {
id: string;
displayName: string;
email: string;
}
export type PaymentStatus = 'pending' | 'completed' | 'failed';
// UserCard.tsx — private type used only here, fine to keep inline
interface UserCardProps {
user: UserProfile;
onSelect: (id: string) => void;
}
enum PaymentStatus {
Pending,
Completed,
Failed,
Refunded,
}
enum UserRole {
Admin,
Editor,
Viewer,
}
new Date("YYYY-MM-DD") - it parses as UTC midnight, causing off-by-one day bugs in local time// WRONG - parses as UTC, shows previous day in US timezones
const date = new Date("2026-01-26"); // Could show Jan 25!
// CORRECT - parse as local time
function parseLocalDate(dateString: string): Date {
const [year, month, day] = dateString.split('-').map(Number);
return new Date(year, month - 1, day);
}