Working with the File System in TypeScript: A Practical Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Work with the File System in TypeScript?

\n\n

Every application sooner or later needs to interact with the file system: save configurations, process uploaded files, maintain logs, or manage projects. TypeScript, as a superset of JavaScript, provides developers with powerful tools for working with files and directories through the fs (File System) module from Node.js.

\n\n

In this article, we will cover the main file system operations in TypeScript using practical examples: reading and writing files, creating and deleting directories, and error handling. All examples will be presented using strict typing, which is a key advantage of TypeScript.

\n\n

Main Modules for Working with the File System

\n\n

Before moving to practice, let's get acquainted with the three main modules needed for the work:

\n\n
    \n
  • fs — the main module for file system operations (reading, writing, deleting)
  • \n
  • fs/promises — a version of the fs module with promise support (recommended for modern code)
  • \n
  • path — a utility for working with file and directory paths
  • \n
\n\n

To import modules in TypeScript, use the following syntax:

\n\n
import * as fs from 'fs';\nimport * as fsPromises from 'fs/promises';\nimport * as path from 'path';
\n\n

Note: for correct work with TypeScript, make sure that in your tsconfig.json file the options "moduleResolution": "node" and "esModuleInterop": true are set.

\n\n

Reading Files: Synchronous and Asynchronous Approaches

\n\n

Synchronous Reading with Typing

\n\n

Synchronous methods block code execution until the operation completes. Use them only for small files or during initial application loading:

\n\n
import * as fs from 'fs';\nimport * as path from 'path';\n\nfunction readConfigSync(): Record<string, unknown> {\n  const configPath = path.join(__dirname, 'config.json');\n  \n  try {\n    const data = fs.readFileSync(configPath, 'utf-8');\n    return JSON.parse(data) as Record<string, unknown>;\n  } catch (error) {\n    if (error instanceof Error) {\n      console.error('Error reading config:', error.message);\n    }\n    return {};\n  }\n}\n\nconst config = readConfigSync();\nconsole.log('Configuration loaded:', config);
\n\n

Asynchronous Reading with async/await (Recommended Method)

\n\n

For production code, always use asynchronous methods. TypeScript works great with async/await:

\n\n
import { readFile } from 'fs/promises';\nimport * as path from 'path';\n\ninterface UserData {\n  id: number;\n  name: string;\n  email: string;\n}\n\nasync function readUserData(fileName: string): Promise<UserData[]> {\n  const filePath = path.resolve('data', fileName);\n  \n  try {\n    const rawData = await readFile(filePath, { encoding: 'utf-8' });\n    const users: UserData[] = JSON.parse(rawData);\n    return users;\n  } catch (error) {\n    if (error instanceof Error) {\n      throw new Error(`Could not read file ${fileName}: ${error.message}`);\n    }\n    throw error;\n  }\n}\n\n// Usage\n(async () => {\n  try {\n    const users = await readUserData('users.json');\n    console.log(`Loaded ${users.length} users`);\n  } catch (error) {\n    console.error(error);\n  }\n})();
\n\n

Writing and Updating Files

\n\n

Creating and Overwriting Files

\n\n

To write data to a file, use the writeFile method. It will create the file if it does not exist, or overwrite an existing one:

\n\n
import { writeFile, mkdir } from 'fs/promises';\nimport * as path from 'path';\n\ninterface LogEntry {\n  timestamp: string;\n  level: 'info' | 'warn' | 'error';\n  message: string;\n}\n                

Blogs

Book Recommendations