Refactoring Legacy Code in TypeScript: A Step-by-Step 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

Refactoring legacy code to TypeScript: a step-by-step guide

Working with legacy code is an inevitable part of any developer's life, especially in large projects. Often such code is written in JavaScript without strict typing, abounding in "magic" strings, obscure functions, and a lack of modularity. Switching to TypeScript is not just a change of syntax, but an opportunity to significantly improve the architecture, readability, and maintainability of the project. In this article, we will break down a proven strategy for refactoring legacy code to TypeScript that minimizes risks and brings maximum benefit.



Why is TypeScript ideal for refactoring legacy code?

TypeScript provides unique capabilities for safe refactoring:

  • Static typing: Allows you to catch errors at compile time, not at runtime.
  • Automatic documentation: Types serve as living documentation, explaining what data a function expects.
  • Improved navigation and autocompletion: The IDE (VS Code, WebStorm) understands the code structure, which speeds up development.
  • Safe refactorings: Renaming variables or changing function signatures can be done with confidence that nothing will break.
  • Gradual adoption: TypeScript can be added to a project file by file, without rewriting everything at once.


Step 1: Preparation and environment setup

Before touching the code, you need to prepare the infrastructure. Without it, refactoring will turn into chaos.

  1. Make sure you have tests. This is the most important rule of refactoring. If there are no tests — write at least integration tests for key modules. Without tests, you risk breaking working functionality.
  2. Set up TypeScript. Install typescript and create a tsconfig.json.
  3. Enable strict checks. In tsconfig.json, be sure to add "strict": true. This enables strictNullChecks, noImplicitAny, and other useful options.

Example of a minimal tsconfig.json:

{  "compilerOptions": {    "target": "ES2020",    "module": "commonjs",    "strict": true,    "esModuleInterop": true,    "skipLibCheck": true,    "outDir": "./dist"  },  "include": ["src/**/*"]}
  1. Set up a linter and formatter. ESLint with the TypeScript plugin (@typescript-eslint) and Prettier will help maintain a consistent code style.


Step 2: Gradual migration from JavaScript to TypeScript

Don't try to rewrite the entire project in one day. Use the "file by file" strategy.

Action algorithm:

  • Rename the file legacyModule.js to legacyModule.ts.
  • Add types for exported and imported functions.
  • Use any temporarily if you are unsure of the type. Mark such places with a comment // TODO: fix type.
  • Run the tests and check that everything works.

Example: Suppose we have a legacy function in JavaScript:

// oldModule.jsfunction calculateDiscount(price, customerType) {    if (customerType === 'vip') {        return price * 0.8;    }    return price * 0.95;}

After refactoring to TypeScript:

// oldModule.tstype CustomerType = 'regular' | 'vip';

function calculateDiscount(price: number, customerType: CustomerType): number { if (customerType === 'vip') { return price * 0.8; } return price * 0.95;}

The CustomerType type makes the code self-documenting.

Blogs

Book Recommendations