Skip to content

Instantly share code, notes, and snippets.

@viniazvd
Last active March 5, 2026 14:08
Show Gist options
  • Select an option

  • Save viniazvd/7102e6b1ff2461829b4c1982394ee0a0 to your computer and use it in GitHub Desktop.

Select an option

Save viniazvd/7102e6b1ff2461829b4c1982394ee0a0 to your computer and use it in GitHub Desktop.
description alwaysApply
JS/TS standards - code style, imports, types, errors, SOLID, Object Calisthenics (no React-specific rules)
true

JS/TS Standards

Single source of truth for code style, architecture, and performance. Apply when generating or reviewing JS/TS code. For React-specific rules, see react-standards.mdc (applies only when working with .tsx/.jsx).

Always standardize and prioritize readability and explicit code in functions, files, and naming.


1. Code Style

  • Prioritize functional, clear, readable code – same input → same output where possible; names and structure should make intent obvious.

1.0 Readability & Explicitness

Area Rule Example
Functions Name describes what it does (verb + noun); one clear responsibility; explicit params and return meaning getUserById, formatCurrency, isOrderEligibleForRefund
Files Name reflects content; one main concern per file; name reads like the module's purpose user-profile.tsx → user profile UI; format-date.ts → date formatting only
Naming No abbreviations; booleans as is/has/can/should; variables and types express domain intent userCount not usrCnt; isLoading, hasError, canSubmit
  • Prefer names that make code self-explanatory; a reader should understand intent without tracing implementation.
  • When in doubt, choose the more explicit name or structure.

1.1 Comments

  • Always in english, lowercase
  • Prefer self-documenting code; comments only when intent is unclear

1.2 File Naming & Size

  • kebab-case for all files: user-profile.tsx, api-utils.ts
  • Keep files under ~350 lines; split into smaller modules when growing

1.3 Variables & Control Flow

  • Prefer const; use let only when reassignment is required
  • No nested if/else/switch; use early returns and guard clauses
  • Prefer map, filter, reduce over imperative loops
  • Ternary for simple conditionals; pure functions for complex logic
  • Named constants for magic numbers and repeated strings; avoid raw literals for domain concepts
  • Don't mutate function arguments or shared state; prefer spread, new arrays, immutable updates
  • Avoid redundant conditionals: use the boolean directly instead of === true/=== false; use array.length instead of array.length > 0 when you only mean "has items" (keep x > 0 when the value can be negative and you mean "positive")
// ❌ redundant
if (isActive === true) submit();
if (items.length > 0) renderList();

// ✅ direct
if (isActive) submit();
if (items.length) renderList();
// ❌ nested, let
let result;
if (user) { if (user.isActive) result = a(); else result = b(); }

// ✅ const, early return + ternary
if (!user) return null;
return user.isActive ? renderDashboard() : renderInactive();
// ❌ magic number
if (status === 3) showSuccess();

// ✅ named constant
const ORDER_STATUS_COMPLETED = 3;
if (status === ORDER_STATUS_COMPLETED) showSuccess();

2. Imports

  • Order: external packages first, then internal/aliases, then relative (group with blank line between)
  • Prefer named imports; avoid import * unless needed (e.g. namespace)
  • No default exports for components/utils when named exports work (better refactor and tree-shake)
// ✅ grouped and ordered
import { useState } from 'react';
import { formatDate } from '@/lib/date';

import { UserCard } from './user-card';

3. Types (TypeScript)

  • Prefer interface for object shapes (extendable); type for unions, intersections, primitives
  • Avoid any; use unknown and narrow with type guards or assertions only when safe
  • Explicit return types on public functions; infer when trivial
// ✅ unknown + guard
const parse = (payload: unknown): User => {
  if (!isUserShape(payload)) throw new ValidationError('Invalid user');
  return payload;
};

// ✅ interface for objects
interface User {
  id: string;
  name: string;
}

4. Errors

  • Use Error or custom subclasses; pass cause when wrapping
  • Never empty catch; at least log and rethrow or handle explicitly
  • Fail fast: validate early and throw with clear messages
// ❌ swallowed
try { await fetchUser(); } catch {}

// ✅ log + rethrow or handle
try {
  await fetchUser();
} catch (e) {
  logger.error('fetchUser failed', { error: e });
  throw new UserFetchError('Could not load user', { cause: e });
}

5. SOLID (React/TS)

Principle Rule React Application
S Single Responsibility One reason to change Split: hooks (data), components (UI), utils (logic)
O Open/Closed Extend without modifying Composition, props/variants; avoid mutating shared state
L Liskov Substitution Subtypes substitutable Component variants honor same props/behavior contract
I Interface Segregation Narrow interfaces Specific props over catch-all options
D Dependency Inversion Depend on abstractions Callbacks/strategies as props; inject services
// ✅ SRP - separation of concerns
const useUser = (id: string) => { /* fetch */ };
const UserCard = ({ user }: { user: User }) => <div>{user.name}</div>;

6. Object Calisthenics

  1. One indentation level – Avoid nesting (e.g. if inside if inside loop). Use early returns and extract small functions so each block has at most one level of indentation. Easier to read and test.

  2. No else – After a return, an else is redundant. Prefer: guard clause → return; then the “happy path” stays at the top level. Use ternary for simple either/or. Keeps code flat and intent clear.

  3. Wrap primitives – When a primitive (number, string) has a specific meaning (e.g. “user id” vs “order id”), use a branded type or a small type so the type system prevents mixing them. Reduces “wrong variable” bugs.

  4. First-class collections – Prefer typed collections (Map, Set, typed arrays) over “array of anything.” They express intent (unique keys → Map; uniqueness → Set) and often give better performance and APIs.

  5. One dot per line (Law of Demeter) – Avoid chains like a.b().c().d(). Each step depends on the previous one and is hard to test or change. Extract intermediate results into named variables or small functions so each line does one thing.

  6. Don't abbreviate – Names should be readable at a glance. Prefer userCount, getUserById over usrCnt, getUsr. Abbreviations save a few characters but cost clarity, especially for newcomers.

  7. Small units – Keep functions and components short (~50 lines or less). If it grows, split by responsibility. Small units are easier to understand, test, and reuse.

  8. Max 2 instance vars – If a component or object has many pieces of state, group related ones into a few objects (e.g. filterState, paginationState). Fewer “flat” variables reduce noise and make relationships clear.

  9. No getters/setters – Prefer methods that perform an action (“tell”) over ones that only return or assign (“ask”). If you only expose data, use plain properties or functions with clear names instead of get/set pairs that hide behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment