In this chapter, we'll explore TypeScript project structure and organization in detail, from the foundational setup to advanced structuring techniques. You'll learn how to configure folders, manage files, set up tsconfig.json, and follow best practices for scalable and maintainable TypeScript projects. By the end, you'll have a solid grasp of how to efficiently structure and organize a TypeScript project.
A TypeScript project structure helps in maintaining code quality, scalability, and ease of collaboration. The project structure defines how the files and directories are organized, how dependencies are managed, and how the code is compiled and tested.
To start a new TypeScript project, you need to have Node.js and npm (Node Package Manager) installed. Once you have these installed, follow these steps:
mkdir my-typescript-project
cd my-typescript-project
npm init -y
npm install typescript --save-dev
tsconfig.json
file:
npx tsc --init
The tsconfig.json
file is essential as it configures the TypeScript compiler.
The tsconfig.json
file is the configuration file for TypeScript. It allows you to define how TypeScript code is compiled.
compilerOptions
: This section specifies compiler options
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src",
"esModuleInterop": true,
"skipLibCheck": true
}
}
target
: Specifies the ECMAScript target version.module
: Specifies the module system (e.g., commonjs
for Node.js).strict
: Enables all strict type-checking options.outDir
: Directory for compiled output.rootDir
: Root directory of the source files.esModuleInterop
: Enables interoperability between CommonJS and ES Modules.skipLibCheck
: Skips type checking of all declaration files (.d.ts
).include
: Specifies the files or directories to be included.
{
"include": ["src/**/*"]
}
exclude
: Specifies the files or directories to be excluded.
{
"exclude": ["node_modules", "dist"]
}
A typical TypeScript project is organized in a way that separates source code, configuration files, and built output.
Here’s a recommended directory structure:
my-typescript-project/
├── src/
│ ├── models/
│ ├── services/
│ ├── utils/
│ └── index.ts
├── tests/
├── dist/
├── node_modules/
├── package.json
├── tsconfig.json
└── README.md
src/
: Contains the source code.models/
: Contains TypeScript interfaces and types.services/
: Contains service classes and functions.utils/
: Contains utility functions.index.ts
: Entry point of the application.tests/
: Contains test files.dist/
: Contains the compiled output.node_modules/
: Contains installed dependencies.package.json
: Contains project metadata and dependencies.tsconfig.json
: TypeScript configuration file.TypeScript supports both ES Modules and Namespaces for organizing code.
ES Modules are the standard for JavaScript modules.
src/models/user.ts
:
export interface User {
id: number;
name: string;
email: string;
}
src/services/userService.ts
:
import { User } from '../models/user';
export class UserService {
getUser(id: number): User {
return { id, name: 'John Doe', email: 'john.doe@example.com' };
}
}
Namespaces are a way to group related code.
src/models/user.ts
:
namespace Models {
export interface User {
id: number;
name: string;
email: string;
}
}
src/services/userService.ts
:
///
namespace Services {
import User = Models.User;
export class UserService {
getUser(id: number): User {
return { id, name: 'John Doe', email: 'john.doe@example.com' };
}
}
}
Dependencies are managed using npm.
To install a package, use:
npm install
For development dependencies, use:
npm install --save-dev
package.json
:
{
"name": "my-typescript-project",
"version": "1.0.0",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"test": "jest"
},
"devDependencies": {
"typescript": "^4.0.0",
"jest": "^26.0.0",
"@types/jest": "^26.0.0"
},
"dependencies": {
"express": "^4.17.1"
}
}
To compile TypeScript code, run:
npx tsc
This uses the tsconfig.json
file to compile the TypeScript files in the src
directory and outputs them to the dist
directory.
To automatically recompile TypeScript code when changes are detected, use:
npx tsc --watch
Jest is a popular testing framework that can be used with TypeScript.
npm install jest @types/jest ts-jest --save-dev
Create a jest.config.js
file:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/tests/**/*.test.ts'],
};
tests/userService.test.ts
:
import { UserService } from '../src/services/userService';
describe('UserService', () => {
it('should return a user by id', () => {
const service = new UserService();
const user = service.getUser(1);
expect(user).toEqual({ id: 1, name: 'John Doe', email: 'john.doe@example.com' });
});
});
To run tests, use:
npm test
Linting and formatting help maintain code quality and consistency.
Installing ESLint:
npm install eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin --save-dev
Create an .eslintrc.js
file:
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended'
],
rules: {
// Add custom rules here
},
};
To lint the code, use:
npx eslint . --ext .ts
Installing Prettier:
npm install prettier --save-dev
Create a .prettierrc
file:
{
"semi": true,
"singleQuote": true,
"trailingComma": "all"
}
Install Prettier ESLint config:
npm install eslint-config-prettier eslint-plugin-prettier --save-dev
Update .eslintrc.js
:
module.exports = {
parser: '@typescript-eslint/parser',
extends: [
'plugin:@typescript-eslint/recommended',
'prettier/@typescript-eslint',
'plugin:prettier/recommended'
],
rules: {
// Add custom rules here
},
};
Initialize a Git repository:
git init
Create a .gitignore
file:
node_modules
dist
.env
Using GitHub Actions for CI:
Create a .github/workflows/ci.yml
file:
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm install
- run: npm run build
- run: npm test
Path mapping helps simplify imports.
tsconfig.json
:
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@models/*": ["src/models/*"],
"@services/*": ["src/services/*"]
}
}
}
With path mapping, you can import modules using aliases:
import { User } from '@models/user';
import { UserService } from '@services/userService';
Environment variables can be managed using the dotenv
package.
dotenv
:
npm install dotenv
dotenv
:Create a .env
file:
API_KEY=your_api_key
Load environment variables in your code:
import * as dotenv from 'dotenv';
dotenv.config();
const apiKey = process.env.API_KEY;
Organizing and structuring a TypeScript project involves setting up the correct directory structure, configuring TypeScript, managing dependencies, and ensuring code quality through testing and linting. By following best practices and utilizing tools like ESLint, Prettier, and Jest, you can maintain a clean and efficient codebase. Happy coding !❤️