wednesday-dev
Technical development guidelines for Wednesday Solutions projects. Enforces import ordering, complexity limits, naming conventions, TypeScript best practices, and code quality standards for React/Next.js applications.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install wednesday-solutions-ai-agent-skills-wednesday-dev
Repository
Skill path: skills/wednesday-dev
Technical development guidelines for Wednesday Solutions projects. Enforces import ordering, complexity limits, naming conventions, TypeScript best practices, and code quality standards for React/Next.js applications.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Frontend.
Target audience: everyone.
License: MIT.
Original source
Catalog source: SkillHub Club.
Repository owner: wednesday-solutions.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install wednesday-dev into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/wednesday-solutions/ai-agent-skills before adding wednesday-dev to shared team environments
- Use wednesday-dev for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw SKILL.md
---
name: wednesday-dev
description: Technical development guidelines for Wednesday Solutions projects. Enforces import ordering, complexity limits, naming conventions, TypeScript best practices, and code quality standards for React/Next.js applications.
license: MIT
metadata:
author: wednesday-solutions
version: "1.0"
compatibility: Next.js 14+, React 18+, TypeScript 5+
---
# Wednesday Technical Development Guidelines
This skill enforces code quality standards for Wednesday Solutions projects. Follow these guidelines to maintain consistency, readability, and maintainability across the codebase.
## 1. Import Order
Imports must follow a strict ordering pattern. The project uses `@trivago/prettier-plugin-sort-imports` to enforce this automatically.
### Required Order
```typescript
// 1. React (always first)
import React, { useState, useEffect } from 'react'
// 2. Next.js imports
import { useRouter } from 'next/navigation'
import Image from 'next/image'
// 3. State management (Recoil, Redux)
import { useSelector } from 'react-redux'
// 4. UI libraries (MUI, Radix)
import { Button } from '@mui/material'
// 5. Path alias imports (@/)
import { useAuth } from '@/lib/hooks/useAuth'
import { API_ROUTES } from '@/lib/constants'
// 6. External packages
import { z } from 'zod'
import { motion } from 'framer-motion'
// 7. Internal components
import { Header } from 'components/Header'
// 8. Relative imports (sibling/parent)
import { utils } from './utils'
import { types } from '../types'
```
### Rules
- Each import group must be separated by a blank line
- Imports within each group should be alphabetically sorted
- Use type imports for TypeScript types: `import type { User } from '@/types'`
- Avoid wildcard imports (`import * as`) unless necessary
## 2. Code Complexity
### Cyclomatic Complexity
**Maximum allowed: 8** (stricter than industry standard of 10)
Functions exceeding this limit must be refactored. See [references/COMPLEXITY.md](references/COMPLEXITY.md) for detailed strategies.
### Quick Remediation
| Complexity | Action Required |
|------------|-----------------|
| 1-4 | Good - easy to test |
| 5-7 | Acceptable - consider simplifying |
| 8 | At limit - refactor if adding logic |
| 9+ | Must refactor before merging |
### How to Reduce Complexity
1. **Extract helper functions** - Break large functions into smaller, focused ones
2. **Use early returns** - Replace nested conditions with guard clauses
3. **Replace conditionals with polymorphism** - Use strategy pattern for complex branching
4. **Simplify boolean expressions** - Extract complex conditions into named variables
5. **Use lookup tables** - Replace switch statements with object maps
```typescript
// BAD: High complexity
function getDiscount(user: User, items: Item[]) {
let discount = 0
if (user.isPremium) {
if (items.length > 10) {
discount = 20
} else if (items.length > 5) {
discount = 10
} else {
discount = 5
}
} else {
if (items.length > 10) {
discount = 10
} else if (items.length > 5) {
discount = 5
}
}
return discount
}
// GOOD: Low complexity with lookup table
const DISCOUNT_TABLE = {
premium: { large: 20, medium: 10, small: 5 },
regular: { large: 10, medium: 5, small: 0 },
} as const
function getCartSize(count: number): 'large' | 'medium' | 'small' {
if (count > 10) return 'large'
if (count > 5) return 'medium'
return 'small'
}
function getDiscount(user: User, items: Item[]) {
const tier = user.isPremium ? 'premium' : 'regular'
const size = getCartSize(items.length)
return DISCOUNT_TABLE[tier][size]
}
```
### File Size Limits
- **Max file lines**: 250
- **Max function lines**: 350
- **Max line length**: 120 characters
## 3. Naming Conventions
### Components & Classes
Use **PascalCase** for React components, classes, types, and interfaces.
```typescript
// Components
function UserProfile() { }
function PaymentCard() { }
// Types & Interfaces
interface UserProfileProps { }
type PaymentMethod = 'card' | 'bank'
// Classes
class AuthService { }
```
### Functions & Variables
Use **camelCase** for functions, variables, hooks, and object properties.
```typescript
// Functions
function fetchUserData() { }
function calculateTotal() { }
// Variables
const isLoading = true
const userProfile = { }
// Hooks
function useAuth() { }
function useLocalStorage() { }
```
### Constants & Enums
Use **UPPER_SNAKE_CASE** for constants and enum values.
```typescript
// Constants
const API_BASE_URL = 'https://api.example.com'
const MAX_RETRY_ATTEMPTS = 3
// Enums
enum UserRole {
ADMIN = 'ADMIN',
USER = 'USER',
GUEST = 'GUEST',
}
```
### Boolean Variables
Prefix with `is`, `has`, `should`, `can`, or `will` for clarity.
```typescript
// Good
const isAuthenticated = true
const hasPermission = false
const shouldRefetch = true
const canEdit = user.role === 'ADMIN'
// Bad
const authenticated = true
const permission = false
const refetch = true
```
### Files & Folders
| Type | Convention | Example |
|------|------------|---------|
| Component files | PascalCase | `UserProfile.tsx` |
| Hook files | camelCase with `use` prefix | `useAuth.ts` |
| Utility files | camelCase | `formatDate.ts` |
| Constant files | camelCase | `apiRoutes.ts` |
| Type files | camelCase | `user.types.ts` |
| Test files | Match source + `.test` | `UserProfile.test.tsx` |
| Folders | camelCase | `components/`, `hooks/` |
### Descriptive Naming
Names should be self-documenting:
```typescript
// Bad - unclear intent
const d = new Date()
const arr = users.filter(u => u.a)
function proc(x: number) { }
// Good - clear intent
const currentDate = new Date()
const activeUsers = users.filter(user => user.isActive)
function processPayment(amount: number) { }
```
## 4. TypeScript Best Practices
### Strict Type Safety
- Enable strict mode in `tsconfig.json`
- Avoid `any` type - use `unknown` and narrow types
- Use explicit return types for public functions
- Leverage discriminated unions for state
```typescript
// Use discriminated unions
type AsyncState<T> =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: Error }
// Narrow types properly
function processValue(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase()
}
if (typeof value === 'number') {
return value.toFixed(2)
}
throw new Error('Unsupported type')
}
```
### Type Imports
Separate type imports from value imports:
```typescript
import { useState } from 'react'
import type { FC, ReactNode } from 'react'
import { fetchUser } from '@/lib/api'
import type { User, UserRole } from '@/types'
```
## 5. React Patterns
### Component Structure
Follow this order within components:
1. Type definitions (props interface)
2. Component function declaration
3. Hooks (useState, useEffect, custom hooks)
4. Derived state / computations
5. Event handlers
6. Effects
7. Return statement (JSX)
```typescript
interface UserCardProps {
userId: string
onSelect?: (user: User) => void
}
export function UserCard({ userId, onSelect }: UserCardProps) {
// 1. Hooks
const [isExpanded, setIsExpanded] = useState(false)
const { data: user, isLoading } = useUser(userId)
// 2. Derived state
const displayName = user ? `${user.firstName} ${user.lastName}` : 'Unknown'
// 3. Event handlers
const handleClick = () => {
setIsExpanded(!isExpanded)
onSelect?.(user)
}
// 4. Early returns for loading/error states
if (isLoading) return <Skeleton />
if (!user) return null
// 5. Main render
return (
<Card onClick={handleClick}>
<CardTitle>{displayName}</CardTitle>
{isExpanded && <CardContent>{user.bio}</CardContent>}
</Card>
)
}
```
### Use Client Directive
Mark client components explicitly:
```typescript
'use client'
import { useState } from 'react'
// ... client-side component
```
## 6. Forbidden Patterns
### No Console Statements
Console statements are forbidden in production code. Use proper logging utilities.
```typescript
// Forbidden
console.log('debug:', data)
console.error('Error:', err)
// Use structured logging or remove before commit
```
### No Magic Numbers/Strings
Extract to named constants:
```typescript
// Bad
if (retryCount > 3) { }
if (status === 'active') { }
// Good
const MAX_RETRIES = 3
const STATUS_ACTIVE = 'active'
if (retryCount > MAX_RETRIES) { }
if (status === STATUS_ACTIVE) { }
```
### No Unused Code
- Remove unused imports (enforced by `eslint-plugin-unused-imports`)
- Delete commented-out code
- Remove unused variables (prefix with `_` only if intentionally unused)
## 7. Testing Requirements
### Unit Tests
- Use Jest with React Testing Library
- Test file naming: `ComponentName.test.tsx`
- Focus on user behavior, not implementation details
```typescript
import { render, screen, fireEvent } from '@testing-library/react'
import { UserCard } from './UserCard'
describe('UserCard', () => {
it('displays user name correctly', () => {
render(<UserCard userId="123" />)
expect(screen.getByText('John Doe')).toBeInTheDocument()
})
it('calls onSelect when clicked', () => {
const onSelect = jest.fn()
render(<UserCard userId="123" onSelect={onSelect} />)
fireEvent.click(screen.getByRole('button'))
expect(onSelect).toHaveBeenCalledWith(expect.objectContaining({ id: '123' }))
})
})
```
### E2E Tests
- Use Playwright for end-to-end tests
- Test file naming: `feature.spec.ts`
- Test critical user flows
## 8. Security Considerations
- Sanitize HTML with DOMPurify before rendering
- Validate all user inputs with Zod schemas
- Never expose sensitive data in client-side code
- Use environment variables for secrets
- Implement CSRF protection for forms
## 9. Performance Guidelines
- Use `next/dynamic` for code splitting
- Implement proper loading states
- Memoize expensive computations with `useMemo`
- Avoid unnecessary re-renders with `React.memo`
- Use image optimization with `next/image`
---
For detailed examples and edge cases, see [references/COMPLEXITY.md](references/COMPLEXITY.md).
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/COMPLEXITY.md
```markdown
# Complexity Reference Guide
This document provides detailed strategies for managing code complexity in Wednesday Solutions projects.
## Understanding Cyclomatic Complexity
Cyclomatic complexity measures the number of linearly independent paths through a function. Each decision point (if, else, case, &&, ||, ternary, catch) adds 1 to the complexity.
### Counting Complexity
```typescript
function example(a: boolean, b: boolean, c: number) { // Base: 1
if (a) { // +1
doSomething()
} else if (b) { // +1
doSomethingElse()
}
switch (c) {
case 1: // +1
handleOne()
break
case 2: // +1
handleTwo()
break
default:
handleDefault()
}
const result = a && b // +1 (short-circuit)
? 'both'
: 'not both' // +1 (ternary)
try {
riskyOperation()
} catch (e) { // +1
handleError(e)
}
return result
}
// Total complexity: 8
```
## Refactoring Strategies
### 1. Extract Early Returns (Guard Clauses)
Transform nested conditions into flat guard clauses.
```typescript
// Before: Complexity 5
function processOrder(order: Order) {
if (order) {
if (order.items.length > 0) {
if (order.isPaid) {
if (order.shippingAddress) {
return shipOrder(order)
} else {
return { error: 'No shipping address' }
}
} else {
return { error: 'Order not paid' }
}
} else {
return { error: 'No items' }
}
} else {
return { error: 'Invalid order' }
}
}
// After: Complexity 4
function processOrder(order: Order) {
if (!order) return { error: 'Invalid order' }
if (order.items.length === 0) return { error: 'No items' }
if (!order.isPaid) return { error: 'Order not paid' }
if (!order.shippingAddress) return { error: 'No shipping address' }
return shipOrder(order)
}
```
### 2. Replace Conditionals with Lookup Tables
Transform switch/if-else chains into object lookups.
```typescript
// Before: Complexity 6
function getStatusColor(status: string): string {
if (status === 'pending') {
return 'yellow'
} else if (status === 'approved') {
return 'green'
} else if (status === 'rejected') {
return 'red'
} else if (status === 'cancelled') {
return 'gray'
} else if (status === 'processing') {
return 'blue'
} else {
return 'black'
}
}
// After: Complexity 1
const STATUS_COLORS: Record<string, string> = {
pending: 'yellow',
approved: 'green',
rejected: 'red',
cancelled: 'gray',
processing: 'blue',
}
function getStatusColor(status: string): string {
return STATUS_COLORS[status] ?? 'black'
}
```
### 3. Extract Complex Conditions
Move boolean expressions into named variables or functions.
```typescript
// Before: Hard to read, complexity embedded in condition
function canUserEditPost(user: User, post: Post) {
if ((user.role === 'admin' || user.role === 'moderator') &&
post.status !== 'archived' &&
(post.authorId === user.id || user.permissions.includes('edit_any'))) {
return true
}
return false
}
// After: Self-documenting, same complexity but readable
function canUserEditPost(user: User, post: Post) {
const isPrivilegedUser = user.role === 'admin' || user.role === 'moderator'
const isPostEditable = post.status !== 'archived'
const hasEditPermission = post.authorId === user.id || user.permissions.includes('edit_any')
return isPrivilegedUser && isPostEditable && hasEditPermission
}
```
### 4. Use Strategy Pattern
Replace complex branching with polymorphism.
```typescript
// Before: Complexity 5
function calculatePrice(item: Item, userType: string) {
let price = item.basePrice
if (userType === 'premium') {
price = price * 0.8
if (item.quantity > 10) {
price = price * 0.95
}
} else if (userType === 'wholesale') {
price = price * 0.7
if (item.quantity > 100) {
price = price * 0.9
}
} else {
if (item.quantity > 5) {
price = price * 0.98
}
}
return price
}
// After: Complexity 1 per function
interface PricingStrategy {
calculate(basePrice: number, quantity: number): number
}
const pricingStrategies: Record<string, PricingStrategy> = {
premium: {
calculate: (basePrice, quantity) => {
const discounted = basePrice * 0.8
return quantity > 10 ? discounted * 0.95 : discounted
},
},
wholesale: {
calculate: (basePrice, quantity) => {
const discounted = basePrice * 0.7
return quantity > 100 ? discounted * 0.9 : discounted
},
},
regular: {
calculate: (basePrice, quantity) => {
return quantity > 5 ? basePrice * 0.98 : basePrice
},
},
}
function calculatePrice(item: Item, userType: string) {
const strategy = pricingStrategies[userType] ?? pricingStrategies.regular
return strategy.calculate(item.basePrice, item.quantity)
}
```
### 5. Decompose Functions
Split large functions into smaller, focused ones.
```typescript
// Before: Complexity 10
function processUserRegistration(data: RegistrationData) {
// Validate email
if (!data.email || !data.email.includes('@')) {
return { error: 'Invalid email' }
}
// Validate password
if (!data.password || data.password.length < 8) {
return { error: 'Password too short' }
}
if (!/[A-Z]/.test(data.password)) {
return { error: 'Password needs uppercase' }
}
if (!/[0-9]/.test(data.password)) {
return { error: 'Password needs number' }
}
// Check existing user
const existing = findUserByEmail(data.email)
if (existing) {
return { error: 'Email already registered' }
}
// Create user
const user = createUser(data)
if (!user) {
return { error: 'Failed to create user' }
}
// Send welcome email
const emailSent = sendWelcomeEmail(user)
if (!emailSent) {
// Log but don't fail
logError('Welcome email failed')
}
return { success: true, user }
}
// After: Each function has complexity 2-3
function validateEmail(email: string): string | null {
if (!email || !email.includes('@')) return 'Invalid email'
return null
}
function validatePassword(password: string): string | null {
if (!password || password.length < 8) return 'Password too short'
if (!/[A-Z]/.test(password)) return 'Password needs uppercase'
if (!/[0-9]/.test(password)) return 'Password needs number'
return null
}
function processUserRegistration(data: RegistrationData) {
const emailError = validateEmail(data.email)
if (emailError) return { error: emailError }
const passwordError = validatePassword(data.password)
if (passwordError) return { error: passwordError }
const existing = findUserByEmail(data.email)
if (existing) return { error: 'Email already registered' }
const user = createUser(data)
if (!user) return { error: 'Failed to create user' }
sendWelcomeEmail(user).catch(logError)
return { success: true, user }
}
```
### 6. Use Optional Chaining and Nullish Coalescing
Reduce null checks with modern JavaScript operators.
```typescript
// Before: Complexity increases with each check
function getUserCity(user: User | null) {
if (user) {
if (user.address) {
if (user.address.city) {
return user.address.city
}
}
}
return 'Unknown'
}
// After: Complexity 1
function getUserCity(user: User | null) {
return user?.address?.city ?? 'Unknown'
}
```
## Cognitive Complexity
Beyond cyclomatic complexity, consider **cognitive complexity** - how hard code is for humans to understand.
### Factors That Increase Cognitive Load
1. **Nesting depth** - Each level of nesting multiplies difficulty
2. **Breaking linear flow** - break, continue, early returns in loops
3. **Multiple conditions** - Complex boolean expressions
4. **Recursion** - Harder to trace mentally
### Good Practices
```typescript
// Avoid deep nesting
// Bad
function processItems(items: Item[]) {
for (const item of items) {
if (item.isValid) {
if (item.type === 'special') {
for (const subItem of item.children) {
if (subItem.active) {
// 4 levels deep - hard to follow
}
}
}
}
}
}
// Good - flatten with early continues
function processItems(items: Item[]) {
for (const item of items) {
if (!item.isValid) continue
if (item.type !== 'special') continue
processSpecialItem(item)
}
}
function processSpecialItem(item: Item) {
for (const subItem of item.children) {
if (!subItem.active) continue
// Process at max 1 level deep
}
}
```
## ESLint Configuration
The project enforces complexity via ESLint:
```javascript
// eslint.config.mjs
{
rules: {
'complexity': ['error', 8],
'max-lines': ['error', 250],
'max-lines-per-function': ['error', 350],
'max-depth': ['error', 4],
'max-nested-callbacks': ['error', 3],
}
}
```
## Tools for Measuring Complexity
1. **ESLint** - Reports violations during development
2. **SonarQube** - Comprehensive code quality analysis
3. **VS Code Extensions** - CodeMetrics, SonarLint
## When to Accept Higher Complexity
Sometimes complexity is unavoidable:
1. **State machines** - Complex by nature, but use libraries like XState
2. **Parsers** - Consider using parser generators
3. **Algorithm implementations** - Document thoroughly, test extensively
4. **Legacy integration** - Isolate complexity, add comprehensive tests
In these cases:
- Add `// eslint-disable-next-line complexity` with justification
- Ensure 100% test coverage for the complex function
- Document the complexity in comments
- Consider if a library could handle it better
```
---
## Skill Companion Files
> Additional files collected from the skill directory layout.
### references/NAMING.md
```markdown
# Naming Conventions Reference
Detailed naming conventions for Wednesday Solutions React/TypeScript projects.
## Quick Reference Table
| Element | Convention | Example |
|---------|------------|---------|
| Components | PascalCase | `UserProfile`, `PaymentCard` |
| Hooks | camelCase with `use` prefix | `useAuth`, `useLocalStorage` |
| Functions | camelCase, verb-first | `fetchUser`, `calculateTotal` |
| Variables | camelCase | `isLoading`, `userData` |
| Constants | UPPER_SNAKE_CASE | `API_BASE_URL`, `MAX_RETRIES` |
| Types/Interfaces | PascalCase | `User`, `PaymentMethod` |
| Enums | PascalCase name, UPPER_SNAKE_CASE values | `UserRole.ADMIN` |
| Files (components) | PascalCase | `UserProfile.tsx` |
| Files (utilities) | camelCase | `formatDate.ts` |
| Folders | camelCase | `components/`, `hooks/` |
| CSS classes | kebab-case (Tailwind) | `text-primary`, `bg-gray-100` |
## Detailed Guidelines
### React Components
```typescript
// Component naming - PascalCase
function UserProfileCard() { }
function PaymentMethodSelector() { }
function NavigationHeader() { }
// Higher-order components - prefix with 'with'
function withAuthentication(Component: FC) { }
function withErrorBoundary(Component: FC) { }
// Render prop components - descriptive name
function DataFetcher({ render }: DataFetcherProps) { }
function MouseTracker({ children }: MouseTrackerProps) { }
```
### Custom Hooks
```typescript
// Always prefix with 'use'
function useAuth() { }
function useLocalStorage<T>(key: string) { }
function usePagination(options: PaginationOptions) { }
function useDebounce<T>(value: T, delay: number) { }
// Query hooks - use 'use' + resource name
function useUser(id: string) { }
function useProducts(filters: ProductFilters) { }
function useOrderHistory(userId: string) { }
// Mutation hooks - use 'use' + action + resource
function useCreateUser() { }
function useUpdateProduct() { }
function useDeleteOrder() { }
```
### Functions
```typescript
// Action functions - start with verb
function fetchUserData() { }
function calculateTotalPrice() { }
function validateEmailFormat() { }
function formatCurrency() { }
function parseApiResponse() { }
// Boolean returning functions - prefix with 'is', 'has', 'can', 'should'
function isValidEmail(email: string): boolean { }
function hasPermission(user: User, action: string): boolean { }
function canEditPost(user: User, post: Post): boolean { }
function shouldRefetch(lastFetched: Date): boolean { }
// Event handlers - prefix with 'handle' or 'on'
function handleSubmit() { }
function handleInputChange() { }
function onUserSelect() { }
function onModalClose() { }
// Transformer functions - 'to' + target format
function toKebabCase(str: string) { }
function toApiFormat(data: FormData) { }
function toDisplayDate(date: Date) { }
// Factory functions - 'create' + object type
function createUser(data: UserData): User { }
function createApiClient(config: Config): ApiClient { }
```
### Variables
```typescript
// Boolean variables - 'is', 'has', 'should', 'can', 'will'
const isAuthenticated = true
const hasErrors = errors.length > 0
const shouldShowModal = isOpen && !isLoading
const canSubmit = isValid && !isSubmitting
const willRedirect = status === 'success'
// Arrays - plural nouns
const users: User[] = []
const selectedItems: Item[] = []
const errorMessages: string[] = []
// Objects - singular nouns
const user: User = { }
const config: Config = { }
const currentState: State = { }
// Counts - suffix with 'Count'
const userCount = users.length
const retryCount = 0
const errorCount = errors.length
// IDs - suffix with 'Id'
const userId = '123'
const orderId = 'abc'
const sessionId = 'xyz'
// Refs - suffix with 'Ref'
const inputRef = useRef<HTMLInputElement>(null)
const containerRef = useRef<HTMLDivElement>(null)
// Timeouts/Intervals - suffix with 'Timeout' or 'Interval'
const fetchTimeout = setTimeout(() => { }, 1000)
const pollingInterval = setInterval(() => { }, 5000)
```
### Constants
```typescript
// API endpoints
const API_BASE_URL = 'https://api.example.com'
const AUTH_ENDPOINT = '/auth/login'
const USERS_ENDPOINT = '/users'
// Configuration values
const MAX_RETRY_ATTEMPTS = 3
const DEFAULT_PAGE_SIZE = 20
const SESSION_TIMEOUT_MS = 30 * 60 * 1000
// Feature flags
const FEATURE_NEW_CHECKOUT = 'feature_new_checkout'
const FEATURE_DARK_MODE = 'feature_dark_mode'
// Error messages
const ERROR_NETWORK_FAILURE = 'Network request failed'
const ERROR_UNAUTHORIZED = 'You are not authorized'
// Validation rules
const MIN_PASSWORD_LENGTH = 8
const MAX_USERNAME_LENGTH = 50
const ALLOWED_FILE_TYPES = ['image/png', 'image/jpeg']
```
### Types and Interfaces
```typescript
// Entity types - noun
interface User {
id: string
email: string
name: string
}
interface Product {
id: string
name: string
price: number
}
// Props interfaces - ComponentName + 'Props'
interface UserProfileProps {
userId: string
onEdit?: () => void
}
interface ButtonProps {
variant?: 'primary' | 'secondary'
size?: 'sm' | 'md' | 'lg'
}
// State types - context + 'State'
interface AuthState {
user: User | null
isAuthenticated: boolean
}
interface FormState {
values: Record<string, any>
errors: Record<string, string>
}
// Action types - context + 'Action'
type AuthAction =
| { type: 'LOGIN'; payload: User }
| { type: 'LOGOUT' }
| { type: 'UPDATE_USER'; payload: Partial<User> }
// Response types - endpoint + 'Response'
interface LoginResponse {
user: User
token: string
}
interface UsersResponse {
users: User[]
total: number
page: number
}
// Request types - endpoint + 'Request'
interface LoginRequest {
email: string
password: string
}
interface CreateUserRequest {
email: string
name: string
role: UserRole
}
// Union types - descriptive of options
type ButtonVariant = 'primary' | 'secondary' | 'danger'
type LoadingState = 'idle' | 'loading' | 'success' | 'error'
type SortDirection = 'asc' | 'desc'
```
### Enums
```typescript
// Enum name - PascalCase
// Enum values - UPPER_SNAKE_CASE
enum UserRole {
ADMIN = 'ADMIN',
MODERATOR = 'MODERATOR',
USER = 'USER',
GUEST = 'GUEST',
}
enum OrderStatus {
PENDING = 'PENDING',
PROCESSING = 'PROCESSING',
SHIPPED = 'SHIPPED',
DELIVERED = 'DELIVERED',
CANCELLED = 'CANCELLED',
}
enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
PATCH = 'PATCH',
DELETE = 'DELETE',
}
```
### File Naming
```
components/
├── UserProfile/
│ ├── index.tsx # Main export
│ ├── UserProfile.tsx # Component
│ ├── UserProfile.test.tsx # Tests
│ ├── UserProfile.styles.ts # Styles (if needed)
│ └── types.ts # Local types
├── ui/
│ ├── Button.tsx
│ ├── Card.tsx
│ └── Input.tsx
hooks/
├── useAuth.ts
├── useLocalStorage.ts
└── usePagination.ts
lib/
├── api/
│ ├── client.ts
│ ├── endpoints.ts
│ └── types.ts
├── utils/
│ ├── formatDate.ts
│ ├── validateEmail.ts
│ └── index.ts
└── constants/
├── apiRoutes.ts
├── errorMessages.ts
└── index.ts
```
## Anti-Patterns to Avoid
```typescript
// DON'T: Hungarian notation
const strName = 'John' // Bad
const name = 'John' // Good
// DON'T: Abbreviations (except common ones)
const usr = getUser() // Bad
const user = getUser() // Good
const idx = items.findIndex() // Acceptable (common)
// DON'T: Single letter variables (except loops)
const d = new Date() // Bad
const currentDate = new Date() // Good
for (let i = 0; i < 10; i++) // Acceptable in loops
// DON'T: Prefix interfaces with 'I'
interface IUser { } // Bad (not idiomatic in TS)
interface User { } // Good
// DON'T: Type suffix on types
type UserType = { } // Bad
type User = { } // Good
// DON'T: Redundant context
class UserClass { } // Bad
class User { } // Good
// DON'T: Negative boolean names
const isNotValid = false // Bad
const isInvalid = false // Acceptable
const isValid = true // Better
```
## Domain-Specific Conventions
For this project (financial/educational platform):
```typescript
// Credit/Payment related
interface CreditCard { }
interface PaymentMethod { }
function calculateEMI() { }
function validatePAN() { }
// Course/Lesson related
interface Course { }
interface Lesson { }
interface LessonProgress { }
function markLessonComplete() { }
// User/Auth related
interface UserProfile { }
interface AuthToken { }
function refreshAuthToken() { }
```
```