Работа с файловой системой в TypeScript: практическое руководство

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: зачем нужна работа с файловой системой в 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';                

Блоги

Рекомендаций по книгам