Введение в работу с файловой системой в TypeScript
Работа с файловой системой — одна из ключевых задач при разработке серверных приложений, утилит и инструментов командной строки. TypeScript, будучи надстройкой над JavaScript, предоставляет все возможности Node.js для взаимодействия с файлами и директориями, но добавляет строгую типизацию, что делает код более надёжным и предсказуемым.
В этой статье мы подробно рассмотрим, как работать с файловой системой в TypeScript: от базовых операций чтения и записи до продвинутых техник, таких как работа с потоками и асинхронная обработка. Все примеры сопровождаются готовым кодом, который вы можете сразу использовать в своих проектах.
Для работы с файловой системой в TypeScript используется встроенный модуль fs (file system) из Node.js. Он предоставляет как синхронные, так и асинхронные методы. В современных проектах рекомендуется использовать асинхронные версии с промисами (fs.promises) для неблокирующего выполнения.
1. Настройка проекта и импорт модулей
Прежде чем начать, убедитесь, что ваш проект настроен для работы с Node.js и TypeScript. Создайте директорию проекта и инициализируйте его:
mkdir fs-typescript-tutorial
cd fs-typescript-tutorial
npm init -y
npm install typescript @types/node --save-dev
npx tsc --init
После настройки, в файле tsconfig.json убедитесь, что параметр module установлен в commonjs или ESNext, а target — в ES2020 или выше.
Теперь создадим файл index.ts и импортируем необходимые модули:
import * as fs from 'fs';
import * as path from 'path';
// Или используем promises версию
import { promises as fsPromises } from 'fs';
Модуль path также важен: он предоставляет утилиты для работы с путями файлов, что делает код кроссплатформенным (Windows vs Unix).
2. Чтение и запись файлов
2.1 Чтение файла
Рассмотрим базовые операции с файлами. Начнём с чтения содержимого файла. Асинхронный подход с использованием async/await — наиболее современный и удобный способ.
import { promises as fs } from 'fs';
async function readFileExample(filePath: string): Promise<string> {
try {
const data = await fs.readFile(filePath, 'utf-8');
return data;
} catch (error) {
console.error('Ошибка при чтении файла:', error);
throw error;
}
}
// Использование
async function main() {
const content = await readFileExample('./example.txt');
console.log('Содержимое файла:', content);
}
main();
Метод readFile принимает путь к файлу и опциональную кодировку (по умолчанию null — возвращает Buffer). Если указать 'utf-8', вернётся строка.
2.2 Запись в файл
Запись данных в файл осуществляется с помощью writeFile. Если файл не существует, он будет создан. Если существует — перезаписан.
import { promises as fs } from 'fs';
async function writeFileExample(filePath: string, data: string): Promise<void> {
try {
await fs.writeFile(filePath, data, 'utf-8');
console.log('Файл успешно записан');
} catch (error) {
console.error('Ошибка при записи файла:', error);
throw error;
}
}
// Дописывание в конец файла (append)
async function appendFileExample(filePath: string, data: string): Promise<void> {
try {
await fs.appendFile(filePath, data + '\n', 'utf-8');
console.log('Данные добавлены в конец файла');
} catch (error) {
console.error('Ошибка при добавлении данных:', error);
throw error;
}
}
2.3 Работа с JSON файлами
Часто в приложениях требуется сохранять и загружать данные в формате JSON. Вот удобная обёртка:
import { promises as fs } from 'fs';
async function readJsonFile<T>(filePath: string): Promise<T> {
const data = await fs.readFile(filePath, 'utf-8');
return JSON.parse(data) as T;
}
async function writeJsonFile<T>(filePath: string, data: T): Promise<void> {
await fs.writeFile(filePath, JSON.stringify(data, null, 2), 'utf-8');
}
// Пример использования
interface Config {
host: string;
port: number;
debug: boolean;
}
async function loadConfig() {
const config = await readJsonFile<Config>('./config.json');
console.log(config.host); // typed access
}
3. Работа с директориями и проверка существования
Помимо работы с отдельными файлами, часто нужно управлять директориями: создавать, удалять, проверять их существование.
3.1 Проверка существования файла или папки
import { promises as fs } from 'fs';
async function exists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath, fs.constants.F_OK);
return true;
} catch {
return false;
}
}
// Или более современный способ (Node.js 14+)
import { access } from 'fs/promises';
import { constants } from 'fs';
async function existsModern(filePath: string): Promise<boolean> {
try {
await access(filePath, constants.F_OK);
return true;
} catch {
return false;
}
}
3.2 Создание и удаление директорий
import { promises as fs } from 'fs';
import * as path from 'path';
// Создание директории (рекурсивно, если нужно)
async function createDirectory(dirPath: string): Promise<void> {
try {
await fs.mkdir(dirPath, { recursive: true });
console.log(`Директория ${dirPath} создана`);
} catch (error) {
console.error('Ошибка при создании директории:', error);
throw error;
}
}
// Удаление директории (рекурсивно)
async function removeDirectory(dirPath: string): Promise<void> {
try {
await fs.rm(dirPath, { recursive: true, force: true });
console.log(`Директория ${dirPath} удалена`);
} catch (error) {
console.error('Ошибка при удалении директории:', error);
throw error;
}
}
// Пример: создание вложенной структуры
async function createNestedStructure() {
const baseDir = path.join(__dirname, 'data', 'logs', '2024');
await createDirectory(baseDir);
const filePath = path.join(baseDir, 'app.log');
await fs.writeFile(file