TypeScript Migration

Converting JavaScript projects to TypeScript step by step

🔄 What is TypeScript Migration?

Migration is the process of converting existing JavaScript code to TypeScript. It's done gradually, allowing you to add type safety incrementally without rewriting everything at once, ensuring smooth transition and improved code quality.


// Before: JavaScript
function greet(name) {
  return "Hello, " + name;
}

// After: TypeScript
function greet(name: string): string {
  return `Hello, ${name}`;
}
                                    

Migration Steps

1️⃣

Setup TypeScript

Install and configure TypeScript

npm install --save-dev typescript
npx tsc --init
2️⃣

Rename Files

Change .js to .ts gradually

# Rename one file at a time
mv index.js index.ts
3️⃣

Add Types

Annotate functions and variables

const age: number = 25;
function add(a: number, b: number) {
  return a + b;
}
4️⃣

Fix Errors

Resolve TypeScript errors

// Fix type mismatches
const value: string = "123";
const num = parseInt(value);

🔹 Step 1: Install TypeScript

Add TypeScript to your existing JavaScript project:

# Install TypeScript
npm install --save-dev typescript

# Install type definitions for Node.js
npm install --save-dev @types/node

# Create TypeScript configuration
npx tsc --init

🔹 Step 2: Configure tsconfig.json

Start with a permissive configuration:

{
  "compilerOptions": {
    "target": "ES6",
    "module": "commonjs",
    "lib": ["ES6", "DOM"],
    "allowJs": true,              // Allow JavaScript files
    "checkJs": false,             // Don't check JS files yet
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": false,              // Start with loose checking
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Tip: Start with strict: false and enable it later once most files are migrated.

🔹 Step 3: Rename Files Gradually

Convert files one at a time from .js to .ts:

🔸 Before: utils.js

// utils.js
function calculateTotal(items) {
  return items.reduce((sum, item) => sum + item.price, 0);
}

function formatCurrency(amount) {
  return `$${amount.toFixed(2)}`;
}

module.exports = { calculateTotal, formatCurrency };

🔸 After: utils.ts

// utils.ts
interface Item {
  price: number;
  name: string;
}

export function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

export function formatCurrency(amount: number): string {
  return `$${amount.toFixed(2)}`;
}

🔹 Step 4: Add Type Annotations

Gradually add types to your code:

🔸 JavaScript Version

class User {
  constructor(name, email, age) {
    this.name = name;
    this.email = email;
    this.age = age;
  }

  getInfo() {
    return `${this.name} (${this.age})`;
  }
}

🔸 TypeScript Version

class User {
  name: string;
  email: string;
  age: number;

  constructor(name: string, email: string, age: number) {
    this.name = name;
    this.email = email;
    this.age = age;
  }

  getInfo(): string {
    return `${this.name} (${this.age})`;
  }
}

🔹 Step 5: Handle Common Migration Issues

🔸 Issue 1: Implicit Any

// ❌ Before: Implicit any
function processData(data) {
  return data.map(item => item.value);
}

// ✅ After: Explicit types
interface DataItem {
  value: number;
}

function processData(data: DataItem[]): number[] {
  return data.map(item => item.value);
}

🔸 Issue 2: Null/Undefined

// ❌ Before: Potential null error
function getUser(id) {
  const user = users.find(u => u.id === id);
  return user.name; // Error if user is undefined
}

// ✅ After: Null checking
function getUser(id: number): string | undefined {
  const user = users.find(u => u.id === id);
  return user?.name;
}

🔸 Issue 3: Module Imports

// ❌ Before: CommonJS
const express = require('express');

// ✅ After: ES6 imports
import express from 'express';

// If types are missing, install them
// npm install --save-dev @types/express

🔹 Step 6: Install Type Definitions

Add type definitions for third-party libraries:

# Common type definitions
npm install --save-dev @types/node
npm install --save-dev @types/express
npm install --save-dev @types/react
npm install --save-dev @types/lodash

# Check if types are available
npm search @types/library-name

🔹 Step 7: Enable Strict Mode Gradually

Once most files are migrated, enable strict checks:

{
  "compilerOptions": {
    // Enable strict checks one by one
    "strict": false,
    "noImplicitAny": true,           // Start here
    "strictNullChecks": true,        // Then this
    "strictFunctionTypes": true,     // Then this
    "strictBindCallApply": true,
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "alwaysStrict": true
  }
}

🔹 Migration Example: Complete File

🔸 Before: JavaScript (app.js)

const express = require('express');
const app = express();

const users = [];

app.get('/users', (req, res) => {
  res.json(users);
});

app.post('/users', (req, res) => {
  const user = req.body;
  users.push(user);
  res.status(201).json(user);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

🔸 After: TypeScript (app.ts)

import express, { Request, Response } from 'express';

interface User {
  id: number;
  name: string;
  email: string;
}

const app = express();
const users: User[] = [];

app.get('/users', (req: Request, res: Response) => {
  res.json(users);
});

app.post('/users', (req: Request, res: Response) => {
  const user: User = req.body;
  users.push(user);
  res.status(201).json(user);
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});

🔹 Migration Best Practices

  • Start Small: Migrate utility files and modules first
  • One File at a Time: Don't try to convert everything at once
  • Use allowJs: Keep JavaScript and TypeScript files together during migration
  • Add Types Gradually: Start with basic types, refine later
  • Test Frequently: Ensure functionality isn't broken
  • Use any Temporarily: It's okay to use any initially, fix later
  • Enable Strict Mode Last: Turn on strict checks after most code is migrated

🔹 Package.json Scripts

Add build scripts for TypeScript:

{
  "scripts": {
    "build": "tsc",
    "watch": "tsc --watch",
    "start": "node dist/index.js",
    "dev": "ts-node src/index.ts"
  }
}

🧠 Test Your Knowledge

What should you do first when migrating JavaScript to TypeScript?