Introduction to Working with the File System in TypeScript
Working with the file system is one of the key tasks when developing in TypeScript. Whether you are creating a server application, a data processing utility, or a web server, you will definitely need to read, write, and manage files and directories. In this guide, we will cover the basics of working with the fs (File System) module in TypeScript, look at synchronous and asynchronous operations, and learn how to handle errors.
TypeScript is a strictly typed superset of JavaScript that adds static typing. When working with the file system, we will use the standard Node.js module fs, but with the additional benefits of TypeScript: autocompletion, type checking, and improved code readability.
Project Setup and Module Imports
Before you begin, make sure you have Node.js and TypeScript installed. Create a new project and install the necessary types:
mkdir fs-typescript-tutorialcd fs-typescript-tutorialnpm init -ynpm install typescript @types/node --save-devnpx tsc --initTo work with the file system, we will need two main modules:
- fs — for performing file operations (reading, writing, deleting, etc.)
- path — for convenient work with paths (joining, normalizing, getting extensions)
Let's import them into our TypeScript file:
import * as fs from 'fs';import * as path from 'path';
// Alternative option — importing only the needed functions:import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';import { join, resolve } from 'path';Note: we use import * as fs because the fs module exports many functions. TypeScript will automatically pick up the types from the installed @types/node package.
Reading and Writing Files
Let's start with the most basic operations — reading and writing text files. In Node.js, there are two approaches: synchronous and asynchronous. For simple scripts and utilities, synchronous methods are often used, while for high-load servers, asynchronous methods are used to avoid blocking the event loop.
Synchronous Reading and Writing
Synchronous methods block program execution until the operation completes. They are simple to use and suitable for scenarios where speed is not critical:
import { readFileSync, writeFileSync } from 'fs';import { join } from 'path';
const filePath = join(__dirname, 'data.txt');
// Writing to a file (creates the file if it doesn't exist)writeFileSync(filePath, 'Hello, TypeScript!', 'utf-8');
// Reading from a fileconst content: string = readFileSync(filePath, 'utf-8');console.log(content); // Outputs: Hello, TypeScript!Note the typing content: string — TypeScript guarantees that we will get a string. If the file does not exist, an exception will be thrown, which needs to be handled with try/catch.
Asynchronous Reading and Writing
Asynchronous methods accept a callback or return a Promise (in modern versions). It is recommended to use Promise versions with 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 { // Asynchronous writing await fsPromises.writeFile(filePath, 'Async content', 'utf-8'); // Asynchronous reading const data: string = await fsPromises.readFile(filePath, 'utf-8'); console.log(data); // Async content } catch (error) { console.error('Error working with file:', error); }}
handleFile();Using async/await makes the code look like syn