阿里云主机折上折
  • 微信号
Current Site:Index > Project structure organization

Project structure organization

Author:Chuan Chen 阅读数:27869人阅读 分类: TypeScript

The Importance of Project Structure Organization

A well-organized project structure significantly enhances code maintainability, team collaboration efficiency, and long-term iteration sustainability. The design of a TypeScript project's structure must consider the characteristics of the type system, modularization approaches, and best practices in modern front-end engineering.

Basic Directory Structure Design

A typical TypeScript project usually includes the following core directories:

project-root/
├── src/                  # Source code directory
│   ├── core/             # Core business logic
│   ├── features/         # Feature modules
│   ├── shared/           # Shared resources
│   └── index.ts          # Entry file
├── tests/                # Test code
├── typings/              # Type declarations
├── configs/              # Build configurations
└── package.json

Modular Structure by Feature

A feature-first modular structure is suitable for medium to large projects:

// Example: User authentication module structure
src/
  auth/
    ├── components/       # Authentication-related UI components
    ├── hooks/           # Custom hooks
    ├── services/        # API services
    ├── types/           # Type definitions
    ├── utils/           # Utility functions
    ├── index.ts         # Module export
    └── auth.test.ts    # Test file

This structure ensures each feature module is self-contained, making it easier to maintain and test individually.

Layered Architecture Practices

Clear layering helps manage code complexity:

// Example of a typical layered structure
src/
  ├── presentation/      # Presentation layer (React components, etc.)
  ├── application/       # Application logic layer
  ├── domain/            # Domain model layer
  └── infrastructure/    # Infrastructure (API calls, etc.)

Each layer communicates through well-defined interfaces. For example, the domain layer defines repository interfaces, while the infrastructure layer implements them:

// domain/repositories/UserRepository.ts
interface UserRepository {
  getUser(id: string): Promise<User>;
}

// infrastructure/repositories/HttpUserRepository.ts
class HttpUserRepository implements UserRepository {
  async getUser(id: string) {
    // Actual HTTP implementation
  }
}

Strategies for Organizing the Type System

TypeScript projects require careful planning for type definitions:

  1. Global types: Place in types/ or src/types.ts.
  2. Module-specific types: Keep them with the module.
  3. Third-party type extensions: Use declaration merging.
// Example of extending a third-party library's types
declare module 'some-library' {
  interface LibraryConfig {
    customOption?: boolean;
  }
}

Structure Design for Configuration Management

Environment and build configurations should be managed separately:

config/
  ├── webpack/          # Webpack configurations
  │   ├── common.config.ts
  │   ├── dev.config.ts
  │   └── prod.config.ts
  └── env/             # Environment variables
      ├── development.ts
      ├── production.ts
      └── test.ts

Writing configurations in TypeScript provides type checking and IntelliSense:

// config/env/development.ts
export default {
  apiBaseUrl: 'http://localhost:3000',
  enableMock: true,
} as const;  // Use `as const` for precise type inference

Organizing Test Code

Test code should mirror the source code structure:

src/
  components/
    Button/
      ├── Button.tsx
      └── Button.test.tsx  # Co-located test

tests/
  integration/            # Integration tests
  e2e/                    # End-to-end tests
  __mocks__/              # Mock files

Example using Jest:

// Example component test
import { render } from '@testing-library/react';
import Button from '../Button';

describe('Button Component', () => {
  it('renders with correct text', () => {
    const { getByText } = render(<Button>Click</Button>);
    expect(getByText('Click')).toBeInTheDocument();
  });
});

Centralized Management of Utility Code

Shared utility functions should be categorized by purpose:

// src/shared/utils/
  ├── array.ts       # Array utilities
  ├── date.ts        # Date handling
  ├── string.ts      # String utilities
  └── validation.ts  # Validation logic

Example utility function implementation:

// src/shared/utils/array.ts
export function chunk<T>(array: T[], size: number): T[][] {
  return Array.from(
    { length: Math.ceil(array.length / size) },
    (_, i) => array.slice(i * size, i * size + size)
  );
}

Organizing Style Resources

CSS-in-TS solutions recommend co-locating styles with components:

components/
  Card/
    ├── Card.tsx
    ├── Card.module.scss
    ├── Card.test.tsx
    └── types.ts

Global styles and themes should be managed separately:

// src/styles/
  ├── theme/         # Theme definitions
  ├── mixins/        # Style mixins
  ├── base.scss      # Base styles
  └── variables.ts   # Style variables (TypeScript)

Multi-Package Management Strategy

Large projects can adopt a monorepo structure:

packages/
  ├── core/           # Core library
  ├── web-app/        # Web application
  ├── mobile-app/     # Mobile application
  └── shared/         # Shared code

Using TypeScript project references:

// tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "baseUrl": ".",
    "paths": {
      "@shared/*": ["packages/shared/src/*"]
    }
  }
}

Documentation and Code Collaboration

Documentation should be part of the project:

docs/
  ├── architecture.md
  ├── api/            # API documentation
  └── examples/       # Code examples

Generating API documentation with TypeDoc:

/**
 * User service interface
 * @remarks
 * Provides all user-related operations
 */
export interface UserService {
  /**
   * Get user by ID
   * @param id - Unique user identifier
   */
  getUser(id: string): Promise<User>;
}

Optimizing Build Output Structure

Output directories should remain clear:

dist/                 # Build output
  ├── esm/            # ES modules
  ├── cjs/            # CommonJS
  ├── types/          # Type declarations
  └── assets/         # Static assets

Defining entry points in package.json:

{
  "main": "dist/cjs/index.js",
  "module": "dist/esm/index.js",
  "types": "dist/types/index.d.ts"
}

Enforcing Code Standards

Use ESLint and Prettier to maintain consistency:

configs/
  ├── eslint/
  │   ├── base.ts
  │   ├── react.ts
  │   └── node.ts
  └── prettier.ts

Example ESLint configuration:

// configs/eslint/base.ts
export default {
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  extends: [
    'eslint:recommended',
    'plugin:@typescript-eslint/recommended',
  ],
  rules: {
    '@typescript-eslint/explicit-function-return-type': 'error'
  }
};

Handling Environment-Specific Differences

Use conditional imports for environment-specific logic:

// src/core/api/client.ts
let apiClient: ApiClient;

if (process.env.NODE_ENV === 'test') {
  apiClient = new MockApiClient();
} else {
  apiClient = new HttpApiClient();
}

export default apiClient;

Organizing Automation Scripts

Complex scripts should be placed in a dedicated directory:

scripts/
  ├── build.ts        # Custom build scripts
  ├── migrate.ts      # Database migrations
  └── seed.ts         # Test data generation

Example script written in TypeScript:

// scripts/migrate.ts
import { migrateDatabase } from '../src/core/database';

async function runMigrations() {
  try {
    await migrateDatabase();
    console.log('Migrations completed');
  } catch (error) {
    console.error('Migration failed:', error);
    process.exit(1);
  }
}

runMigrations();

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:泛型设计模式

下一篇:编译配置详解

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.