TypeScript and Files: From Beginner to Pro in 15 Minutes

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

Why Working with Files in TypeScript Is Not Scary, But Convenient

\n\n

When I first started writing in TypeScript, the thought scared me: "What about files? In the browser it's simple, but here — server, disks, paths...". But as it turned out, working with files and the file system in TypeScript is like building with LEGO: there are clear pieces (the fs and path modules), there are instructions (typing), and the result is predictable. No surprises, just clean code.

\n\n

In this article, we'll figure out how to read, write, rename, and delete files using modern TypeScript for Node.js. You'll learn why you should use fs/promises instead of the good old fs, how not to get confused with paths, and how to handle errors gracefully. Let's go!

\n\n

Preparation: What You Need to Know Before Starting

\n\n

Before diving into the code, make sure you have Node.js installed (version 14+ — it already has native promises). Also create a simple project:

\n\n
mkdir ts-files-demo\ncd ts-files-demo\nnpm init -y\nnpm install typescript @types/node --save-dev\nnpx tsc --init
\n\n

In the tsconfig.json file, be sure to enable "module": "NodeNext" or "ESNext" and "target": "ES2022". Now you're ready for working with the file system in TypeScript.

\n\n

Imports That Will Always Come in Handy

\n\n

The most correct way to import modules for working with files today is:

\n\n
import { readFile, writeFile, unlink, mkdir, access } from 'fs/promises';\nimport { join, dirname, basename, extname } from 'path';\nimport { fileURLToPath } from 'url';
\n\n

Note: we are not using fs with callbacks. Only fs/promises — this is the modern standard that works perfectly with async/await and TypeScript typing.

\n\n

Reading a File: From Simple to Safe

\n\n

Let's start with the classic — reading a text file. Let's say we have data.txt with some content.

\n\n
import { readFile } from 'fs/promises';\n\nasync function readTextFile(filePath: string): Promise<string> {\n  try {\n    const content = await readFile(filePath, 'utf-8');\n    return content;\n  } catch (error) {\n    console.error('Error reading file:', error);\n    throw error;\n  }\n}\n\n// Usage\nconst data = await readTextFile('./data.txt');\nconsole.log(data);
\n\n

Simple, right? But in real projects, we often work with JSON. Let's make a universal function with a generic:

\n\n
import { readFile } from 'fs/promises';\n\nasync function readJsonFile<T>(filePath: string): Promise<T> {\n  const raw = await readFile(filePath, 'utf-8');\n  return JSON.parse(raw) as T;\n}\n\n// Example: reading a config\ninterface Config {\n  port: number;\n  dbUrl: string;\n}\n\nconst config = await readJsonFile<Config>('./config.json');\nconsole.log(config.port); // type number — TypeScript will hint it automatically
\n\n

This is working with files in TypeScript at its best: typing protects you from silly mistakes, and the code remains readable.

\n\n

Writing to a File: Creating and Updating Data

\n\n

Now let's learn how to save information. The writeFile function overwrites the file if it exists, or creates a new one.

\n\n
import { writeFile, mkdir } from 'fs/promises';\nimport { dirname } from 'path';\n\nasync function saveToFile(filePath: string, data: string): Promise<void> {\n  // First, make sure the folder exists\n  const dir = dirname(filePath);\n  await mkdir(dir, { recursive: true });\n\n  // Now write\n  await writeFile(filePath, data, 'utf-8');\n  console.log(`File ${filePath} saved successfully`);\n}\n\n// Saving an object as JSON\nconst user = { name: 'Anna', age: 28, role: 'admin' };\n                

Blogs

Book Recommendations