- Introduction
- Directory Structure
- Main Application File
- Modules and Subdirectories
- Type Definitions
- Exporting and Importing
- Benefits of Using ES Modules (ESM)
This guide provides a comprehensive overview of how to organize a TypeScript project for maintainability and scalability. It includes directory structures, file naming conventions, and best practices for exporting and importing modules.
A well-organized directory structure helps maintain a clean and understandable codebase. Here is an example structure for a TypeScript project:
/src
/components
/header
header.ts
structures.ts
index.ts
/footer
footer.ts
structures.ts
index.ts
index.ts
/utils
date.ts
generateId.ts
index.ts
app.ts
structures.ts
The main application file, app.ts
, serves as the entry point for your application. It typically contains the core logic to start your application.
55555typescript // app.ts
import { Header, HeaderConfig } from './components/header/index.js'; import { Footer, FooterConfig } from './components/footer/index.js'; import { formatDate, generateId, getCurrentYear } from './utils/index.js';
const headerConfig: HeaderConfig = { title: 'Welcome' }; const header = new Header(headerConfig); header.render();
const footerConfig: FooterConfig = { text: '© 2023 Company' }; const footer = new Footer(footerConfig); footer.render();
const userId = generateId(); console.log('Generated User ID:', userId);
const formattedDate = formatDate(new Date()); console.log('Formatted Date:', formattedDate);
const currentYear = getCurrentYear(); console.log('Current Year:', currentYear); 55555
Each module or component lives in its own directory. Use an index.ts
file in each directory to export the module's public API.
/components/header
header.ts
structures.ts
index.ts
55555typescript // components/header/header.ts
import { HeaderConfig } from './structures.js';
export class Header { private config: HeaderConfig;
constructor(config: HeaderConfig) { this.config = config; }
render() { console.log('Rendering header with config:', this.config); } } 55555
55555typescript // components/header/structures.ts
export interface HeaderConfig { title: string; subtitle?: string; } 55555
55555typescript // components/header/index.ts
export { Header } from './header.js'; export { HeaderConfig } from './structures.js'; 55555
/components/footer
footer.ts
structures.ts
index.ts
55555typescript // components/footer/footer.ts
import { FooterConfig } from './structures.js';
export class Footer { private config: FooterConfig;
constructor(config: FooterConfig) { this.config = config; }
render() { console.log('Rendering footer with config:', this.config); } } 55555
55555typescript // components/footer/structures.ts
export interface FooterConfig { text: string; } 55555
55555typescript // components/footer/index.ts
export { Footer } from './footer.js'; export { FooterConfig } from './structures.js'; 55555
/utils
date.ts
generateId.ts
index.ts
55555typescript // utils/date.ts
export function formatDate(date: Date): string { return date.toISOString().split('T')[0]; }
export function getCurrentYear(): number { return new Date().getFullYear(); } 55555
55555typescript // utils/generateId.ts
export function generateId(): string { return Math.random().toString(36).substr(2, 9); } 55555
55555typescript // utils/index.ts
export { formatDate, getCurrentYear } from './date.js'; export { generateId } from './generateId.js'; 55555
Place shared types in a structures.ts
file within each directory. This keeps type definitions close to where they are used.
Use index.ts
files to re-export modules and types from each directory. When importing, be explicit about what you are importing to keep the code clear and maintainable.
55555typescript // components/index.ts
export { Header, HeaderConfig } from './header/index.js'; export { Footer, FooterConfig } from './footer/index.js'; 55555
55555typescript // app.ts
import { Header, HeaderConfig, Footer, FooterConfig } from './components/index.js'; import { formatDate, getCurrentYear, generateId } from './utils/index.js';
const headerConfig: HeaderConfig = { title: 'Welcome' }; const header = new Header(headerConfig); header.render();
const footerConfig: FooterConfig = { text: '© 2023 Company' }; const footer = new Footer(footerConfig); footer.render();
const userId = generateId(); console.log('Generated User ID:', userId);
const formattedDate = formatDate(new Date()); console.log('Formatted Date:', formattedDate);
const currentYear = getCurrentYear(); console.log('Current Year:', currentYear); 55555
ES Modules (ESM) and CommonJS are both module systems used in JavaScript, but ESM is the standardized module system defined by ECMAScript, while CommonJS is the older module system primarily used in Node.js.
- Standardization: ESM is the official standard for JavaScript modules, ensuring consistency across different environments and platforms.
- Tree Shaking: ESM supports tree shaking, allowing bundlers to remove unused code from the final bundle, which can lead to smaller and more efficient builds.
- Static Analysis: ESM enables better static analysis of imports and exports, improving tooling support, such as IDE features, linting, and code refactoring.
- Better Compatibility: As the web moves towards using ESM, using it in your projects ensures better compatibility with modern tools and libraries.
For more information on the differences and benefits, you can read Node.js documentation on ESM.
This Gist was created with the assistance of ChatGPT.