Введение в работу с файловой системой в TypeScript
Работа с файловой системой — одна из ключевых задач при разработке на TypeScript. Независимо от того, создаёте ли вы серверное приложение, утилиту для обработки данных или веб-сервер, вам обязательно понадобится читать, записывать и управлять файлами и директориями. В этом руководстве мы разберём основы работы с модулем fs (File System) в TypeScript, рассмотрим синхронные и асинхронные операции, а также научимся обрабатывать ошибки.
TypeScript — это строго типизированная надстройка над JavaScript, которая добавляет статическую типизацию. При работе с файловой системой мы будем использовать стандартный модуль Node.js fs, но с дополнительными преимуществами TypeScript: автодополнением, проверкой типов и улучшенной читаемостью кода.
Настройка проекта и импорт модулей
Прежде чем начать, убедитесь, что у вас установлен Node.js и TypeScript. Создайте новый проект и установите необходимые типы:
mkdir fs-typescript-tutorial
cd fs-typescript-tutorial
npm init -y
npm install typescript @types/node --save-dev
npx tsc --init
Для работы с файловой системой нам понадобятся два основных модуля:
- fs — для выполнения операций с файлами (чтение, запись, удаление и т.д.)
- path — для удобной работы с путями (объединение, нормализация, получение расширения)
Импортируем их в наш TypeScript-файл:
import * as fs from 'fs';
import * as path from 'path';
// Альтернативный вариант — импорт только нужных функций:
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
import { join, resolve } from 'path';
Обратите внимание: мы используем import * as fs, так как модуль fs экспортирует множество функций. TypeScript автоматически подхватит типы из установленного пакета @types/node.
Чтение и запись файлов
Начнём с самых базовых операций — чтения и записи текстовых файлов. В Node.js есть два подхода: синхронный и асинхронный. Для простых скриптов и утилит часто используют синхронные методы, а для высоконагруженных серверов — асинхронные, чтобы не блокировать event loop.
Синхронное чтение и запись
Синхронные методы блокируют выполнение программы до завершения операции. Они просты в использовании и подходят для сценариев, где скорость не критична:
import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
const filePath = join(__dirname, 'data.txt');
// Запись в файл (создаёт файл, если его нет)
writeFileSync(filePath, 'Hello, TypeScript!', 'utf-8');
// Чтение из файла
const content: string = readFileSync(filePath, 'utf-8');
console.log(content); // Выведет: Hello, TypeScript!
Обратите внимание на типизацию content: string — TypeScript гарантирует, что мы получим строку. Если файл не существует, будет выброшено исключение, которое нужно обработать через try/catch.
Асинхронное чтение и запись
Асинхронные методы принимают колбэк или возвращают Promise (в современных версиях). Рекомендуется использовать Promise-версии с async/await:
import { promises as fsPromises } from 'fs';
import { join } from 'path';
const filePath = join(__dirname, 'async-data.txt');
async function handleFile(): Promise<void> {
try {
// Асинхронная запись
await fsPromises.writeFile(filePath, 'Async content', 'utf-8');
// Асинхронное чтение
const data: string = await fsPromises.readFile(filePath, 'utf-8');
console.log(data); // Async content
} catch (error) {
console.error('Ошибка при работе с файлом:', error);
}
}
handleFile();
Использование async/await делает код похожим на синхронный, но при этом не блокирует выполнение. Тип Promise<void> явно указывает, что функция возвращает промис.
Работа с директориями и проверка существования
Часто нужно не только читать файлы, но и управлять папками: создавать, удалять, проверять их наличие. Рассмотрим основные операции.
Создание директорий
Для создания одной папки используем mkdir, а для вложенных структур — опцию recursive: true:
import { mkdirSync, existsSync } from 'fs';
import { join } from 'path';
const dirPath = join(__dirname, 'my-folder', 'sub-folder');
// Проверяем, существует ли директория
if (!existsSync(dirPath)) {
// Создаём вложенную структуру папок
mkdirSync(dirPath, { recursive: true });
console.log('Директория создана');
} else {
console.log('Директория уже существует');
}
Асинхронный вариант:
import { promises as fsPromises } from 'fs';
async function createDir(): Promise<void> {
await fsPromises.mkdir(dirPath, { recursive: true });
}
Чтение содержимого директории
Чтобы получить список файлов и папок внутри директории, используем readdir:
import { readdirSync } from 'fs';
const items: string[] = readdirSync(__dirname);
console.log('Содержимое текущей папки:', items);
// Выведет: ['data.txt', 'my-folder', 'index.ts', ...]
TypeScript автоматически выводит тип string[] для массива имён. Если нужно получить полную информацию о каждом элементе (размер, дата создания), используйте readdirSync с опцией { withFileTypes: true } — тогда вы получите массив объектов Dirent.
Проверка типа файла
Часто требуется узнать, является ли элемент файлом или папкой. Для этого используем stat:
import { statSync } from 'fs';
const targetPath = join(__dirname, 'my-folder');
const stats = statSync(targetPath);
if (stats.isFile()) {
console.log('Это файл');
} else if (stats.isDirectory()) {
console.log('Это папка');
}
console.log('Размер:', stats.size, 'байт');
console.log('Дата создания:', stats.birthtime);