2025-12-23
3 min read
Knowdust Team
typescript
javascript
migration
types
refactoring

Migrating JavaScript Projects to TypeScript

A step-by-step guide to gradually migrating existing JavaScript projects to TypeScript.

TypeScript Migration: From JavaScript to Type Safety

Migrating a JavaScript project to TypeScript can seem daunting, but with a gradual approach, it's manageable and beneficial. This guide walks through the migration process step by step.

Why Migrate to TypeScript?

TypeScript offers several advantages:

  • Type Safety: Catch errors at compile time
  • Better IDE Support: Enhanced autocomplete and refactoring
  • Self-Documenting Code: Types serve as documentation
  • Easier Refactoring: Confidence when making changes
  • Future-Proof: Aligns with modern JavaScript features

Preparation Phase

1. Install TypeScript

npm install --save-dev typescript @types/node

2. Create tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": false,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

3. Update package.json Scripts

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

Gradual Migration Strategy

Phase 1: Rename Files (.js to .ts)

Start with files that have minimal dependencies:

// Before: utils.js
function add(a, b) {
  return a + b;
}

// After: utils.ts
function add(a: number, b: number): number {
  return a + b;
}

Phase 2: Add Type Annotations

Gradually add types to function parameters and return values:

// Before
function fetchUser(id) {
  return api.get(`/users/${id}`);
}

// After
function fetchUser(id: string): Promise<User> {
  return api.get(`/users/${id}`);
}

Phase 3: Define Interfaces

Create interfaces for objects and data structures:

interface User {
  id: string;
  name: string;
  email: string;
  createdAt: Date;
}

interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

Common Migration Challenges

Third-Party Libraries

Install type definitions or create ambient declarations:

npm install --save-dev @types/lodash

// For libraries without types
declare module 'some-library' {
  export function someFunction(param: string): boolean;
}

Dynamic Imports

TypeScript needs help with dynamic imports:

const module = await import('./module') as typeof import('./module');

Configuration Files

Some config files might need special handling:

// webpack.config.ts
import * as webpack from 'webpack';

const config: webpack.Configuration = {
  // ... config
};

export default config;

Testing During Migration

Run Tests Frequently

Ensure your test suite passes after each migration step.

Type Checking

Use TypeScript's --noEmit flag for type checking without compilation:

npx tsc --noEmit

Advanced Configuration

Strict Mode

Enable strict mode gradually:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "strictFunctionTypes": true
  }
}

Path Mapping

Configure path aliases:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    }
  }
}

Best Practices

Start Small

Migrate one file at a time, starting with utilities and types.

Use any Temporarily

When stuck, use any as a temporary solution:

// Temporary
const data: any = fetchData();

// Better
interface Data {
  // define proper types
}
const data: Data = fetchData();

Enable Linting

Use TSLint or ESLint with TypeScript rules.

Tools and Resources

Migration Tools

  • ts-migrate: Automated migration tool
  • TypeScript Playground: Test TypeScript code
  • TypeScript Handbook: Official documentation

Learning Resources

  • TypeScript in 5 Minutes
  • Handbook: Basic Types
  • Advanced Types documentation

Conclusion

Migrating to TypeScript is an investment that pays dividends in code quality and developer experience. Take it slow, test frequently, and don't hesitate to use any temporarily when needed. The type safety and better tooling will make your codebase more maintainable and less error-prone.

Published on 2025-12-23

Stay Updated

Get notified when we publish new articles and guides about development tools and best practices.