Project structure organization
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:
- Global types: Place in
types/
orsrc/types.ts
. - Module-specific types: Keep them with the module.
- 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