← Back to TypeScript Course | Chapter 23: Performance and Best Practices | Lesson 7 of 7

Code Organization

Good TypeScript organization separates related responsibilities and makes dependencies easier to understand. Clear module boundaries, exports, and naming help projects remain maintainable as they grow.

Organize by Responsibility

Grouping files by feature or responsibility (e.g. a users/ folder with its types, logic, and tests together) scales better than grouping by file type, since related changes stay co-located instead of scattered across types/, services/, and components/ folders.

Example: Organize by Responsibility

typescript
// users/types.ts, users/service.ts, users/user.test.ts
// grouped together rather than scattered across types/, services/, tests/
console.log("Feature-based folders keep related files co-located");

Use Named Exports

Named exports make it clear at the import site exactly what's being pulled in and make refactoring safer, since renaming a default export doesn't trigger any warning at its call sites the way renaming a named export does.

Example: Use Named Exports

typescript
export function formatDate(date: Date): string {
  return date.toISOString();
}
export const APP_NAME = "MyApp";
console.log(formatDate(new Date()), APP_NAME);

Separate Types from Implementation When Useful

Separating a module's types into their own file makes sense once multiple files need to import just the shape without pulling in the implementation that produces it, avoiding unnecessary coupling.

Example: Separate Types from Implementation When Useful

typescript
// user-types.ts
interface User { id: number; name: string; }
// user-service.ts would import just the User type from user-types.ts
const u: User = { id: 1, name: "Ravi" };
console.log(u);

Avoid Circular Dependencies

Circular dependencies — where module A imports from module B which imports back from A — can cause values to be undefined at import time due to evaluation order, so structuring imports as a one-directional graph avoids a whole class of hard-to-debug bugs.

Example: Avoid Circular Dependencies

typescript
// a.ts imports from b.ts, b.ts imports from a.ts -> undefined at import time
// Fix: extract shared code into a third module both can import from.
console.log("Structure imports as a one-directional graph");

Keep Entry Points Small

Keeping an entry point file (like index.ts) limited to re-exports and wiring, rather than actual logic, makes it easy to see a module's full public surface at a glance without reading implementation details.

Example: Keep Entry Points Small

typescript
// index.ts
export { formatDate } from "./date-utils";
export { UserService } from "./user-service";
console.log("index.ts only re-exports, no real logic lives here");
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.