TS Project Architecture
Good TypeScript architecture is like a well-planned city — organized, navigable, and sustainable.
As TypeScript projects grow, code organization becomes critical. A well-architected TypeScript project is like a well-planned city. Hyderabad was built organically — the old city has narrow, winding streets because it grew without a plan. But HITEC City and the financial district were PLANNED — wide roads, designated zones, clear layouts. When your TypeScript project starts small, it often grows like the old city — files placed wherever, imports going in circles, types scattered everywhere. But as it scales, you need intentional architecture — like Hyderabad's planned suburbs.
A well-organized TypeScript project has:
- Clear boundaries between types, logic, and UI — like residential, commercial, and industrial zones
- Consistent naming conventions — like numbered streets in Madhapur
- Barrel exports for clean imports — like main roads connecting different areas
- Separation of concerns — each module has a single purpose
- Feature-based organization rather than type-based — put files together by feature, not by role
Just like Hyderabad's city planning makes it easy to navigate from Charminar to Gachibowli, good TypeScript architecture makes it easy to navigate your codebase — find the right file, understand the structure, and make changes without breaking unrelated parts. This is the final stage of our course — putting everything together into a coherent, maintainable project structure that will serve you well as your TypeScript journey continues.
Let's look at recommended folder structures for different types of TypeScript projects.
Small to Medium Project:
src/
├── types/ # Shared type definitions
│ ├── user.ts
│ ├── order.ts
│ └── index.ts # Barrel exports
├── utils/ # Pure utility functions
│ ├── format.ts
│ ├── validation.ts
│ └── index.ts
├── services/ # Business logic & API
│ ├── userService.ts
│ └── orderService.ts
├── components/ # UI components
│ ├── UserCard.tsx
│ └── OrderList.tsx
├── hooks/ # Custom React hooks
├── store/ # State management
├── config/ # Configuration
├── constants/ # Constants & enums
├── app.ts # App entry
└── index.ts # Main barrel
Feature-Based (Large Project):
src/
├── features/ # Feature modules
│ ├── users/
│ │ ├── components/
│ │ ├── services/
│ │ ├── types/
│ │ └── index.ts
│ ├── orders/
│ │ ├── components/
│ │ ├── services/
│ │ ├── types/
│ │ └── index.ts
│ └── auth/
├── shared/ # Shared across features
│ ├── ui/ # Generic components
│ ├── utils/ # Utility functions
│ └── types/ # Global types
├── app.ts
└── index.ts
Key Configuration Files:
tsconfig.json # Strict mode, paths, etc.
tsconfig.build.json # Production build config
.eslintrc.js # Linting rules
.prettierrc # Code formatting
jest.config.ts # Test configuration
vitest.config.ts # Alternative test runnerBarrel exports and module design are crucial for a clean TypeScript codebase.
Barrel Export Pattern (index.ts)
// types/index.ts — barrel export
export type { User } from "./user";
export type { Order } from "./order";
export type { ApiResponse } from "./api";
// Another file imports cleanly:
import type {
User,
Order,
} from "../types";
// One import, multiple types!
Module Design Principles
// Each module should:
// 1. Have a SINGLE responsibility
// 2. Export a CLEAR public API
// 3. Keep internals PRIVATE
// 4. Be TESTABLE in isolation
// 5. Have MINIMAL dependencies
// Example — good module:
export function formatPrice(
price: number,
currency = "INR"
): string {
return new Intl.NumberFormat(
"en-IN",
{ style: "currency", currency }
).format(price);
}
// Clear purpose, no side effects,
// easy to test
Type-Only Imports (for better performance)
// Use import type for types only:
import type { User } from "./types";
// This is removed at compile time!
// Use import for values:
import { formatUser } from "./utils";
// Combined:
import type { User } from "./types";
import { formatUser } from "./utils";
Path Aliases (clean imports)
// tsconfig.json:
{
"compilerOptions": {
"paths": {
"@types/*": ["./src/types/*"],
"@utils/*": ["./src/utils/*"],
"@features/*": [
"./src/features/*"
]
}
}
}
// Import with alias:
import { User } from "@types/user";
import { formatDate } from "@utils/date";Let's cover the most common architecture mistakes that plague TypeScript projects.
Trap 1: Circular Dependencies — A imports from B, B imports from A. Extract shared code into a separate module.
Trap 2: Over-Eager Barrels — Exporting everything from everywhere creates import spaghetti. Be selective.
Trap 3: God Files — A single file with 1000+ lines doing everything. Split by concern.
Trap 4: Type Duplication — Same type defined in multiple places. Create shared types.
Trap 5: Missing Index Files — Deep relative imports like ../../../../types/user. Use barrel files and path aliases.
Recommended Structure:
src/
├── features/
│ ├── feature1/ # Self-contained module
│ └── feature2/
├── shared/
│ ├── types/
│ ├── utils/
│ └── ui/
├── app.ts
└── index.ts
Key Principles:
- Feature-based organization for large projects
- Barrel exports (index.ts) for clean imports
- Single Responsibility Principle per module
- Type-only imports using import type
- Path aliases for clean relative imports
- Avoid circular dependencies
- Colocate types with usage
- Consistent naming (PascalCase for types, camelCase for values)
The Golden Rule: "Good TypeScript architecture is like city planning for Hyderabad. Feature folders are like HITEC City neighborhoods — self-contained but well-connected. Barrel exports are the main roads — they make navigation easy. Path aliases are the signboards — they tell you exactly where you are. Plan your code architecture like the city planners planned Hyderabad's tech district — intentional, organized, and built to scale, bhai!"
🎉 Congratulations! You have completed the entire TypeScript Hyderabad Style course! From basic types to project architecture — you now have the skills to write professional, type-safe TypeScript code. The Charminar level is yours, bhai! 🎉
Key Takeaways
- Organize code by FEATURE, not by type — feature folders scale better than type folders
- Use barrel exports (index.ts) to provide clean, single-point imports for each module
- Apply Single Responsibility Principle: each file/module does ONE thing well
- Use import type for type-only imports — they are erased at compile time for better performance
- Configure path aliases in tsconfig.json for clean, maintainable import paths
- Avoid circular dependencies by extracting shared code into separate modules
Want to track your progress?
Log in to save your place and pick up where you left off.
Progress track karna chahte ho?
Login karo apni progress save karne ke liye aur jahan chhoda tha wahan se shuru karo.
Login