Modern Project Structure Implementation

1. Monorepo Nx Lerna Workspace Setup

Manage multiple related packages in a single repository with shared dependencies, unified tooling, and efficient builds.

Tool Key Features Best For Learning Curve
Nx Smart rebuilds, computation caching, code generators, dependency graph Large teams, enterprise apps Medium-High
Turborepo Fast builds, remote caching, simple config, Vercel integration Modern apps, Vercel users Low-Medium
Lerna Package versioning, publishing, bootstrap, legacy support Library authors, npm packages Medium
npm/yarn/pnpm workspaces Built-in, simple, no extra tools Small-medium projects Low

Example: Nx monorepo setup with React and shared libraries

// Initialize Nx workspace
npx create-nx-workspace@latest myorg

// Structure
myorg/
├── apps/
   ├── web/                    # Next.js app
   ├── mobile/                 # React Native app
   └── admin/                  # Admin dashboard
├── libs/
   ├── shared/
   ├── ui/                 # Shared UI components
   ├── utils/              # Utility functions
   └── types/              # TypeScript types
   ├── features/
   ├── auth/               # Authentication feature
   └── payments/           # Payment feature
   └── data-access/
       └── api/                # API client
├── tools/                      # Custom scripts
├── nx.json                     # Nx configuration
├── tsconfig.base.json          # Base TypeScript config
└── package.json

// nx.json - Configuration
{
  "tasksRunnerOptions": {
    "default": {
      "runner": "nx/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "test", "lint"],
        "parallel": 3
      }
    }
  },
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "cache": true
    }
  }
}

// tsconfig.base.json - Path mapping
{
  "compilerOptions": {
    "paths": {
      "@myorg/shared/ui": ["libs/shared/ui/src/index.ts"],
      "@myorg/shared/utils": ["libs/shared/utils/src/index.ts"],
      "@myorg/features/auth": ["libs/features/auth/src/index.ts"]
    }
  }
}

// Generate new library
nx generate @nx/react:library shared/ui

// Generate new app
nx generate @nx/next:application admin

// Run commands
nx build web                    # Build web app
nx test shared-ui               # Test UI library
nx run-many --target=test --all # Test all projects
nx affected:build               # Build only affected by changes
nx dep-graph                    # Visualize dependency graph

Example: Turborepo setup (simpler alternative)

// Initialize Turborepo
npx create-turbo@latest

// Structure
my-turborepo/
├── apps/
│   ├── web/                    # Next.js app
│   └── docs/                   # Documentation site
├── packages/
│   ├── ui/                     # Shared UI components
│   ├── eslint-config/          # Shared ESLint config
│   ├── tsconfig/               # Shared TS configs
│   └── typescript-config/
├── turbo.json                  # Turbo configuration
└── package.json

// turbo.json - Pipeline configuration
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": [".next/**", "dist/**"]
    },
    "test": {
      "dependsOn": ["build"],
      "cache": false
    },
    "lint": {
      "outputs": []
    },
    "dev": {
      "cache": false,
      "persistent": true
    }
  }
}

// Root package.json
{
  "name": "my-turborepo",
  "private": true,
  "workspaces": ["apps/*", "packages/*"],
  "scripts": {
    "build": "turbo run build",
    "dev": "turbo run dev",
    "test": "turbo run test",
    "lint": "turbo run lint"
  },
  "devDependencies": {
    "turbo": "latest"
  }
}

// packages/ui/package.json
{
  "name": "@repo/ui",
  "version": "0.0.0",
  "main": "./src/index.tsx",
  "types": "./src/index.tsx",
  "exports": {
    ".": "./src/index.tsx",
    "./button": "./src/Button.tsx"
  }
}

// apps/web - Import from workspace
import { Button } from '@repo/ui';
import { formatDate } from '@repo/utils';

Example: pnpm workspaces (lightweight approach)

// pnpm-workspace.yaml
packages:
  - 'apps/*'
  - 'packages/*'

// Root package.json
{
  "name": "monorepo",
  "private": true,
  "scripts": {
    "build": "pnpm -r --filter='./packages/*' build",
    "test": "pnpm -r test",
    "dev": "pnpm --parallel -r dev"
  }
}

// packages/shared/package.json
{
  "name": "@monorepo/shared",
  "version": "1.0.0",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch"
  }
}

// apps/web/package.json
{
  "name": "web",
  "dependencies": {
    "@monorepo/shared": "workspace:*"
  }
}

// Commands
pnpm install                    # Install all deps
pnpm --filter web dev           # Run web app
pnpm --filter @monorepo/shared build  # Build shared package
pnpm -r build                   # Build all packages recursively
Choosing a Tool: Use Nx for large teams with complex dependencies. Use Turborepo for modern apps with simple needs. Use pnpm workspaces for minimal overhead. Use Lerna for publishing npm packages.

2. Feature-based Folder Structure

Organize code by feature/domain rather than technical layer for better scalability and team collaboration.

Structure Type Organization Pros Cons
Feature-based Group by business feature Scalable, clear ownership, easy to delete Shared code requires careful planning
Layer-based Group by technical layer (components, hooks, utils) Simple, familiar, flat structure Hard to scale, unclear ownership
Hybrid Features + shared folder for common code Best of both worlds More complex initially
src/
├── features/
│   ├── auth/
│   │   ├── components/
│   │   │   ├── LoginForm.tsx
│   │   │   ├── SignupForm.tsx
│   │   │   └── PasswordReset.tsx
│   │   ├── hooks/
│   │   │   ├── useAuth.ts
│   │   │   └── useLogin.ts
│   │   ├── api/
│   │   │   └── authApi.ts
│   │   ├── types/
│   │   │   └── auth.types.ts
│   │   ├── utils/
│   │   │   └── validation.ts
│   │   └── index.ts              # Barrel export
│   │
│   ├── dashboard/
│   │   ├── components/
│   │   │   ├── DashboardLayout.tsx
│   │   │   ├── StatsCard.tsx
│   │   │   └── RecentActivity.tsx
│   │   ├── hooks/
│   │   │   └── useDashboardData.ts
│   │   ├── api/
│   │   │   └── dashboardApi.ts
│   │   └── index.ts
│   │
│   ├── products/
│   │   ├── components/
│   │   │   ├── ProductList.tsx
│   │   │   ├── ProductCard.tsx
│   │   │   └── ProductDetail.tsx
│   │   ├── hooks/
│   │   │   ├── useProducts.ts
│   │   │   └── useProductFilters.ts
│   │   ├── api/
│   │   │   └── productsApi.ts
│   │   ├── store/
│   │   │   └── productsSlice.ts
│   │   └── index.ts
│   │
│   └── cart/
│       ├── components/
│       ├── hooks/
│       ├── api/
│       └── index.ts

├── shared/
│   ├── components/              # Reusable UI components
│   │   ├── Button/
│   │   ├── Input/
│   │   ├── Modal/
│   │   └── Layout/
│   ├── hooks/                   # Generic hooks
│   │   ├── useDebounce.ts
│   │   ├── useLocalStorage.ts
│   │   └── useMediaQuery.ts
│   ├── utils/                   # Generic utilities
│   │   ├── format.ts
│   │   ├── validation.ts
│   │   └── api.ts
│   ├── types/                   # Global types
│   │   └── common.types.ts
│   └── constants/
│       └── config.ts

├── pages/                       # Next.js pages or React Router routes
│   ├── index.tsx
│   ├── dashboard.tsx
│   └── products/
│       ├── [id].tsx
│       └── index.tsx

├── styles/                      # Global styles
│   ├── globals.css
│   └── variables.css

├── lib/                         # External library configs
│   ├── axios.ts
│   └── react-query.ts

└── App.tsx
// Colocate tests, stories, styles with components
src/features/auth/components/LoginForm/
├── LoginForm.tsx
├── LoginForm.test.tsx
├── LoginForm.stories.tsx
├── LoginForm.module.css
├── useLoginForm.ts              # Component-specific hook
└── index.ts

// LoginForm/index.ts - Barrel export
export { LoginForm } from './LoginForm';
export type { LoginFormProps } from './LoginForm';

// Usage in other files
import { LoginForm } from '@/features/auth/components/LoginForm';

// Alternative flat structure for simple components
src/features/auth/components/
├── LoginForm.tsx
├── LoginForm.test.tsx
├── LoginForm.stories.tsx
├── SignupForm.tsx
└── SignupForm.test.tsx

Example: Next.js App Router with features

app/
├── (auth)/                      # Route group
│   ├── login/
│   │   └── page.tsx             # Uses LoginForm from features/auth
│   └── signup/
│       └── page.tsx

├── dashboard/
│   ├── page.tsx                 # Uses Dashboard from features/dashboard
│   ├── layout.tsx
│   └── settings/
│       └── page.tsx

├── products/
│   ├── page.tsx
│   └── [id]/
│       └── page.tsx

├── api/
│   ├── auth/
│   │   └── route.ts
│   └── products/
│       └── route.ts

├── layout.tsx
└── page.tsx

// Separation of concerns:
// - features/ = business logic, UI, hooks
// - app/ = routing, server components, metadata
// - lib/ = configurations, clients
Anti-pattern: Don't create deep nesting (max 3-4 levels). Don't mix feature code with shared code. Don't use index.ts for everything (explicit imports are clearer). Avoid circular dependencies between features.

3. Barrel Exports Index Files

Use index.ts files to create clean public APIs for modules and simplify imports, but avoid performance pitfalls.

Pattern Use Case Benefits Cautions
Feature Barrel Export feature public API Clean imports, encapsulation Can break tree-shaking
Component Barrel Export component + types Single import point Import cost for all
Namespace Barrel Group related utilities Organized exports All-or-nothing imports
Direct Exports Performance-critical code Best tree-shaking Longer import paths

Example: Feature barrel export pattern

// features/auth/index.ts - Public API
export { LoginForm } from './components/LoginForm';
export { SignupForm } from './components/SignupForm';
export { useAuth } from './hooks/useAuth';
export { useLogin } from './hooks/useLogin';
export type { User, AuthState, LoginCredentials } from './types/auth.types';

// Don't export internal utilities
// import { validateEmail } from './utils/validation'; ❌

// Consumer code
import { LoginForm, useAuth } from '@/features/auth';

// features/auth/components/index.ts - Component barrel
export { LoginForm } from './LoginForm/LoginForm';
export { SignupForm } from './SignupForm/SignupForm';
export { PasswordReset } from './PasswordReset/PasswordReset';

// Usage
import { LoginForm, SignupForm } from '@/features/auth/components';

Example: Tree-shaking friendly exports

// ❌ Bad - Breaks tree-shaking (imports everything)
// shared/utils/index.ts
export * from './format';
export * from './validation';
export * from './api';
export * from './date';

// Usage imports ALL utils even if only using one
import { formatCurrency } from '@/shared/utils';

// ✅ Good - Named exports (better tree-shaking)
// shared/utils/index.ts
export { formatCurrency, formatDate } from './format';
export { validateEmail, validatePhone } from './validation';
export { apiClient, handleApiError } from './api';

// ✅ Better - Direct imports (best tree-shaking)
import { formatCurrency } from '@/shared/utils/format';
import { validateEmail } from '@/shared/utils/validation';

// ✅ Best - Namespace exports for related functions
// shared/utils/format/index.ts
export const format = {
  currency: (value: number) => `${value.toFixed(2)}`,
  date: (date: Date) => date.toLocaleDateString(),
  percent: (value: number) => `${(value * 100).toFixed(2)}%`
};

// Usage
import { format } from '@/shared/utils/format';
format.currency(100); // "$100.00"

Example: Component library barrel exports

// shared/components/index.ts - Component library
export { Button } from './Button';
export { Input } from './Input';
export { Modal } from './Modal';
export { Card } from './Card';
export type { ButtonProps } from './Button';
export type { InputProps } from './Input';

// Usage
import { Button, Card, type ButtonProps } from '@/shared/components';

// Alternative: Subpath exports in package.json
// package.json
{
  "exports": {
    ".": "./src/index.ts",
    "./button": "./src/Button.tsx",
    "./input": "./src/Input.tsx",
    "./modal": "./src/Modal.tsx"
  }
}

// Usage with subpaths (better performance)
import { Button } from '@repo/ui/button';
import { Input } from '@repo/ui/input';
Best Practice: Use barrel exports for feature boundaries and component libraries. Use direct imports for utilities and helpers. Configure sideEffects: false in package.json for better tree-shaking.

4. Absolute Imports Path Mapping

Configure absolute imports to avoid relative path hell and improve code readability with TypeScript path mapping.

Configuration Tool/Framework Config File Syntax
TypeScript Paths All TS projects tsconfig.json @/*, @components/*
Webpack Aliases CRA, custom webpack webpack.config.js resolve.alias
Next.js Next.js projects tsconfig.json (auto) @/* by default
Vite Vite projects vite.config.ts resolve.alias

Example: TypeScript path mapping configuration

// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/shared/components/*"],
      "@features/*": ["src/features/*"],
      "@hooks/*": ["src/shared/hooks/*"],
      "@utils/*": ["src/shared/utils/*"],
      "@types/*": ["src/shared/types/*"],
      "@lib/*": ["src/lib/*"],
      "@styles/*": ["src/styles/*"]
    }
  }
}

// Before (relative imports - hard to read)
import Button from '../../../shared/components/Button';
import { formatDate } from '../../../../shared/utils/format';
import { useAuth } from '../../features/auth/hooks/useAuth';

// After (absolute imports - clean and clear)
import Button from '@components/Button';
import { formatDate } from '@utils/format';
import { useAuth } from '@features/auth/hooks/useAuth';

// Alternative: Single @ prefix
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

// Usage
import Button from '@/shared/components/Button';
import { formatDate } from '@/shared/utils/format';
import { useAuth } from '@/features/auth/hooks/useAuth';

Example: Vite configuration with aliases

// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '@components': path.resolve(__dirname, './src/shared/components'),
      '@features': path.resolve(__dirname, './src/features'),
      '@hooks': path.resolve(__dirname, './src/shared/hooks'),
      '@utils': path.resolve(__dirname, './src/shared/utils'),
      '@types': path.resolve(__dirname, './src/shared/types'),
      '@styles': path.resolve(__dirname, './src/styles')
    }
  }
});

// tsconfig.json (must match Vite config)
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"],
      "@components/*": ["src/shared/components/*"],
      "@features/*": ["src/features/*"],
      "@hooks/*": ["src/shared/hooks/*"],
      "@utils/*": ["src/shared/utils/*"],
      "@types/*": ["src/shared/types/*"],
      "@styles/*": ["src/styles/*"]
    }
  }
}

Example: Next.js automatic path mapping

// tsconfig.json - Next.js auto-configures @/* alias
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@/lib/*": ["./src/lib/*"]
    }
  }
}

// Usage in Next.js
// app/page.tsx
import { Button } from '@/components/ui/Button';
import { db } from '@/lib/database';

// Monorepo with multiple apps
// apps/web/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "paths": {
      "@web/*": ["./src/*"],
      "@repo/ui": ["../../packages/ui/src/index.ts"],
      "@repo/utils": ["../../packages/utils/src/index.ts"]
    }
  }
}

Example: Jest configuration for path mapping

// jest.config.js - Match TypeScript paths
module.exports = {
  moduleNameMapper: {
    "^@/(.*)$": "<rootDir>/src/$1",
    "^@components/(.*)$": "<rootDir>/src/shared/components/$1",
    "^@features/(.*)$": "<rootDir>/src/features/$1",
    "^@hooks/(.*)$": "<rootDir>/src/shared/hooks/$1",
    "^@utils/(.*)$": "<rootDir>/src/shared/utils/$1"
  }
};

// Alternative: Use ts-jest preset (auto-reads tsconfig paths)
module.exports = {
  preset: 'ts-jest',
  moduleNameMapper: {
    "^@/(.*)\\$": "<rootDir>/src/$1"
  }
};
Common Pitfalls: Ensure build tools (Webpack, Vite, Jest) configurations match tsconfig.json paths. Use relative imports for files in same feature. Don't mix @ and ~ prefixes. Keep path mappings simple (2-3 max).

5. ESLint Prettier Husky Configuration

Enforce code quality and consistency with automated linting, formatting, and pre-commit hooks.

Tool Purpose When It Runs Config File
ESLint Find code quality issues, enforce rules On save, pre-commit, CI .eslintrc.json
Prettier Format code consistently On save, pre-commit .prettierrc
Husky Git hooks automation Pre-commit, pre-push .husky/
lint-staged Run linters on staged files only Pre-commit .lintstagedrc

Example: Complete ESLint + Prettier + Husky setup

// Installation
npm install -D eslint prettier eslint-config-prettier eslint-plugin-prettier
npm install -D @typescript-eslint/parser @typescript-eslint/eslint-plugin
npm install -D eslint-plugin-react eslint-plugin-react-hooks
npm install -D husky lint-staged

// Initialize Husky
npx husky init

// .eslintrc.json
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:react/recommended",
    "plugin:react-hooks/recommended",
    "prettier" // Must be last to override other configs
  ],
  "parser": "@typescript-eslint/parser",
  "parserOptions": {
    "ecmaVersion": 2022,
    "sourceType": "module",
    "ecmaFeatures": {
      "jsx": true
    }
  },
  "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"],
  "rules": {
    "prettier/prettier": "error",
    "@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "@typescript-eslint/explicit-module-boundary-types": "off",
    "react/react-in-jsx-scope": "off", // Not needed in React 17+
    "react/prop-types": "off", // Using TypeScript
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  },
  "settings": {
    "react": {
      "version": "detect"
    }
  }
}

// .prettierrc
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 100,
  "tabWidth": 2,
  "useTabs": false,
  "arrowParens": "avoid",
  "endOfLine": "lf"
}

// .prettierignore
node_modules
.next
dist
build
coverage
*.min.js

Example: Husky + lint-staged configuration

// package.json
{
  "scripts": {
    "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
    "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
    "format": "prettier --write \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
    "format:check": "prettier --check \"src/**/*.{ts,tsx,js,jsx,json,css,md}\"",
    "prepare": "husky"
  },
  "lint-staged": {
    "*.{ts,tsx,js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,css,md}": [
      "prettier --write"
    ]
  }
}

// .husky/pre-commit
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

// .husky/pre-push
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run type-check
npm run test

// .lintstagedrc.js (alternative config file)
module.exports = {
  '*.{ts,tsx}': [
    'eslint --fix',
    'prettier --write',
    () => 'tsc --noEmit' // Type check all files
  ],
  '*.{js,jsx}': ['eslint --fix', 'prettier --write'],
  '*.{json,md,css}': ['prettier --write']
};

Example: Next.js ESLint configuration

// .eslintrc.json - Next.js specific
{
  "extends": [
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
    "prettier"
  ],
  "rules": {
    "@next/next/no-html-link-for-pages": "off",
    "@typescript-eslint/no-explicit-any": "warn",
    "@typescript-eslint/no-unused-vars": ["error", { 
      "argsIgnorePattern": "^_",
      "varsIgnorePattern": "^_"
    }]
  }
}

// VS Code settings.json - Auto-format on save
{
  "editor.formatOnSave": true,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}
Best Practice: Run prettier through ESLint with eslint-plugin-prettier. Use lint-staged to only check changed files (faster). Configure editor to format on save. Add type-check to pre-push hook.

6. TypeScript Strict Mode Configuration

Enable TypeScript strict mode for maximum type safety and catch errors at compile-time instead of runtime.

Strict Option What It Does Common Issues It Catches
strict Enables all strict checks (recommended) All type-related issues
strictNullChecks null/undefined must be explicit Null pointer exceptions
strictFunctionTypes Strict function parameter checking Function type mismatches
noImplicitAny No implicit any types Missing type annotations
noUnusedLocals Error on unused variables Dead code, typos
noImplicitReturns All code paths must return Missing return statements

Example: Strict TypeScript configuration

// tsconfig.json - Recommended strict settings
{
  "compilerOptions": {
    // Strict Type Checking
    "strict": true,                           // Enable all strict options
    "noImplicitAny": true,                    // No implicit 'any'
    "strictNullChecks": true,                 // Strict null/undefined checks
    "strictFunctionTypes": true,              // Strict function types
    "strictBindCallApply": true,              // Strict bind/call/apply
    "strictPropertyInitialization": true,     // Class properties must be initialized
    "noImplicitThis": true,                   // No implicit 'this'
    "alwaysStrict": true,                     // Parse in strict mode
    
    // Additional Checks
    "noUnusedLocals": true,                   // Error on unused variables
    "noUnusedParameters": true,               // Error on unused parameters
    "noImplicitReturns": true,                // All paths must return
    "noFallthroughCasesInSwitch": true,       // No fallthrough in switch
    "noUncheckedIndexedAccess": true,         // Index signatures return T | undefined
    "allowUnreachableCode": false,            // Error on unreachable code
    
    // Module Resolution
    "moduleResolution": "bundler",            // Modern resolution
    "resolveJsonModule": true,                // Import JSON files
    "isolatedModules": true,                  // Each file as separate module
    "esModuleInterop": true,                  // Better CommonJS interop
    "skipLibCheck": true,                     // Skip type checking .d.ts files
    
    // Emit
    "target": "ES2022",
    "lib": ["ES2022", "DOM", "DOM.Iterable"],
    "jsx": "react-jsx",                       // React 17+ JSX transform
    "module": "ESNext",
    "sourceMap": true,
    
    // Path Mapping
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist", "build"]
}

Example: Strict null checks and proper typing

// ❌ Without strictNullChecks
function getUser(id: string) {
  const user = database.findUser(id); // Could be undefined
  return user.name; // Runtime error if user is undefined
}

// ✅ With strictNullChecks
function getUser(id: string): string | undefined {
  const user = database.findUser(id); // User | undefined
  
  // Must handle null/undefined explicitly
  if (!user) {
    return undefined;
  }
  
  return user.name;
}

// ✅ Better - Use optional chaining
function getUserName(id: string): string | undefined {
  const user = database.findUser(id);
  return user?.name; // Safe navigation
}

// ✅ Best - Use nullish coalescing
function getUserNameOrDefault(id: string): string {
  const user = database.findUser(id);
  return user?.name ?? 'Anonymous';
}

// ❌ noImplicitAny error
function process(data) { // Error: Parameter 'data' implicitly has 'any' type
  return data.value;
}

// ✅ Fixed with explicit type
function process(data: { value: string }): string {
  return data.value;
}

// ✅ Generic for reusability
function process<T>(data: { value: T }): T {
  return data.value;
}

Example: Class strictPropertyInitialization

// ❌ Without strictPropertyInitialization
class User {
  name: string; // No error, but undefined at runtime
  email: string;
}

// ✅ Fixed - Initialize in constructor
class User {
  name: string;
  email: string;
  
  constructor(name: string, email: string) {
    this.name = name;
    this.email = email;
  }
}

// ✅ Alternative - Definite assignment assertion (use carefully)
class User {
  name!: string; // ! tells TS we'll initialize it
  email!: string;
  
  async init(id: string) {
    const data = await fetchUser(id);
    this.name = data.name;
    this.email = data.email;
  }
}

// ✅ Best - Use optional or union types
class User {
  name?: string;           // Explicitly optional
  email: string | null;    // Explicitly nullable
  
  constructor(email: string | null) {
    this.email = email;
  }
}

// React component with strict mode
interface Props {
  user?: User;  // Explicitly optional
  onSave: (data: User) => void;
}

function UserProfile({ user, onSave }: Props) {
  if (!user) {
    return <div>No user data</div>;
  }
  
  // TypeScript knows user is defined here
  return <div>{user.name}</div>;
}

Example: noUncheckedIndexedAccess

// Without noUncheckedIndexedAccess
const colors = ['red', 'green', 'blue'];
const color = colors[5]; // color: string (wrong!)
console.log(color.toUpperCase()); // Runtime error

// ✅ With noUncheckedIndexedAccess enabled
const colors = ['red', 'green', 'blue'];
const color = colors[5]; // color: string | undefined (correct!)

if (color) {
  console.log(color.toUpperCase()); // Safe
}

// Dictionary access
interface UserMap {
  [id: string]: User;
}

const users: UserMap = { '1': { name: 'John' } };

// Without noUncheckedIndexedAccess
const user = users['999']; // user: User (wrong!)

// With noUncheckedIndexedAccess
const user = users['999']; // user: User | undefined (correct!)
if (user) {
  console.log(user.name);
}

Project Structure Best Practices

Aspect Recommendation Rationale
Monorepo Nx (enterprise), Turborepo (modern), pnpm (simple) Choose based on team size and complexity
Folder Structure Feature-based with shared folder Scalable, clear ownership, easy refactoring
Barrel Exports Use for features, avoid for utilities Clean API, but watch bundle size
Path Mapping @ prefix with 2-3 aliases max Clean imports without path hell
Code Quality ESLint + Prettier + Husky + lint-staged Automated consistency, catch errors early
TypeScript strict: true with all checks enabled Maximum type safety, fewer runtime errors