Введение: зачем нужна работа с файловой системой в TypeScript
TypeScript, будучи строго типизированной надстройкой над JavaScript, предоставляет разработчикам мощные инструменты для работы с файловой системой. Независимо от того, создаёте ли вы утилиту для обработки логов, веб-сервер с загрузкой файлов или инструмент для миграции данных — умение эффективно взаимодействовать с файловой системой является базовым навыком.
В этой статье мы рассмотрим практические аспекты работы с файловой системой в TypeScript с использованием встроенного модуля fs (File System) из Node.js. Все примеры будут сопровождаться подробными комментариями и объяснениями. Мы разберём синхронные и асинхронные методы, работу с путями и типизацию данных.
1. Настройка проекта и импорт модуля fs
Прежде чем приступить к работе, необходимо настроить TypeScript проект. Убедитесь, что у вас установлен Node.js и менеджер пакетов npm. Создайте новый проект и установите типы для Node.js:
npm init -y
npm install typescript @types/node --save-dev
npm install ts-node --save-dev
Создайте файл tsconfig.json и добавьте в него базовые настройки:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*"]
}
Теперь в вашем файле index.ts импортируйте модули fs и path:
import * as fs from 'fs';
import * as path from 'path';
Важно: В TypeScript мы используем import * as для импорта всего модуля. Это обеспечивает полную типизацию всех методов.
2. Чтение файлов: синхронный и асинхронный подходы
2.1 Асинхронное чтение (рекомендуемый способ)
В TypeScript предпочтительнее использовать асинхронные методы, так как они не блокируют основной поток выполнения. Рассмотрим пример чтения текстового файла:
import * as fs from 'fs/promises';
import * as path from 'path';
async function readFileExample(filePath: string): Promise<string> {
try {
const absolutePath = path.resolve(filePath);
const content: string = await fs.readFile(absolutePath, 'utf-8');
return content;
} catch (error) {
if (error instanceof Error) {
throw new Error(`Ошибка чтения файла: ${error.message}`);
}
throw error;
}
}
// Использование
(async () => {
const data = await readFileExample('./data/example.txt');
console.log('Содержимое файла:', data);
})();
Обратите внимание на использование fs/promises — это версия модуля с промисами, которая отлично интегрируется с async/await в TypeScript.
2.2 Синхронное чтение (для простых сценариев)
Иногда, например, при загрузке конфигурации при старте, можно использовать синхронные методы:
import * as fs from 'fs';
import * as path from 'path';
function readConfigSync(configPath: string): Record<string, unknown> {
const fullPath = path.join(__dirname, configPath);
const rawData = fs.readFileSync(fullPath, 'utf-8');
return JSON.parse(rawData);
}
const config = readConfigSync('./config.json');
console.log('Конфигурация:', config);
3. Запись и модификация файлов
3.1 Запись данных в файл
Для записи данных в файл используйте writeFile. TypeScript позволяет строго типизировать данные перед записью:
import * as fs from 'fs/promises';
interface UserData {
id: number;
name: string;
email: string;
}
async function saveUserData(users: UserData[]): Promise<void> {
const jsonString: string = JSON.stringify(users, null, 2);
await fs.writeFile('./users.json', jsonString, 'utf-8');
console.log('Данные успешно сохранены');
}
const users: UserData[] = [
{ id: 1, name: 'Иван Иванов', email: 'ivan@example.com' },
{ id: 2, name: 'Мария Петрова', email: 'maria@example.com' }
];
saveUserData(users);
3.2 Дозапись в файл (append)
Если нужно добавить данные в конец существующего файла, используйте appendFile:
import * as fs from 'fs/promises';
async function appendToLog(logEntry: string): Promise<void> {
const timestamp: string = new Date().toISOString();
const entry: string = `[${timestamp}] ${logEntry}\n`;
await fs.appendFile('./app.log', entry, 'utf-8');
}
// Пример использования
appendToLog('Приложение запущено');
appendToLog('Пользователь авторизован');
4. Работа с директориями и проверка существования
4.1 Создание и удаление папок
Часто требуется создавать вложенные структуры папок. Для этого используйте mkdir с опцией recursive: true:
import * as fs from 'fs/promises';
import * as path from 'path';
async function createProjectStructure(basePath: string): Promise<void> {
const folders: string[] = ['src', 'src/components', 'public', 'data'];
for (const folder of folders) {
const fullPath = path.join(basePath, folder);
await fs.mkdir(fullPath, { recursive: true });
console.log(`Создана папка: ${fullPath}`);
}
}
createProjectStructure('./my-project');
4.2 Проверка существования файла или папки
Вместо устаревшего fs.exists используйте fs.access или fs.stat:
import * as fs from 'fs/promises';
async function checkPathExists(targetPath: string): Promise<boolean> {
try {
await fs.access(targetPath);
return true;
} catch {
return false;
}
}
async function getFileInfo(targetPath: string): Promise<{ exists: boolean; isFile: boolean; size: number }> {
try {
const stats = await fs.stat(targetPath);
return {
exists: true,
isFile: stats.isFile(),
size: stats.size // в байтах
};
} catch {
return { exists: false, isFile: false, size: 0 };
}
}
// Пример использования
(async () => {
const filePath = './data/example.txt';