The Reality of JavaScript to TypeScript Migration

Every migration guide starts with "just rename .js to .ts." That's like saying "just land the rocket" - technically correct but useless when you hit real problems. After helping dozens of teams migrate codebases from 10K to 500K lines, here's what actually happens and how to fix it.

Migration Timeline Reality Check

TypeScript Asset Pack

Small projects (< 5K lines): anywhere from a few days to a month depending on how much weird JS you have
Medium projects (5K-50K lines): 2-4 months with incremental approach, longer if you hit framework integration hell
Large projects (50K+ lines): 6-12 months minimum, plan for longer when you discover all the dynamic code

That GitHub project you saw migrate 100K lines in "2 weeks"? They had 8 engineers working full-time on migration and nothing else. Plan accordingly. Check real migration timelines from companies like Airbnb and Slack.

The Fatal Migration Mistakes

Teams try to migrate everything at once. The TypeScript compiler throws 500+ errors and your engineers will want to murder you. Half the team gives up and wants to revert.

The Fix: Do it file by file. Start with the easy stuff - utility functions, constants. Work your way up. Don't mix type fixes with feature work or you'll go insane.

Mistake 2: Fighting Every Error Right Away
You rename 50 files to .ts and suddenly have 200 compiler errors. Everyone tries to fix every single error immediately. Development stops for weeks and your PM starts asking uncomfortable questions.

The Fix: Use @ts-ignore and any everywhere at first. Get the damn thing compiling, then fix types gradually. Better to ship with some any types than get stuck for months. One team I consulted spent 3 weeks fixing type errors in their date formatting utils while their customers waited for a critical bug fix. They could have shipped with // @ts-ignore and fixed the types later.

Mistake 3: Perfect Types from Day One
Teams enable strict: true immediately because "we want to do TypeScript properly." Compiler errors multiply exponentially. Team burns out fighting generic type constraints nobody understands.

The Fix: Start with strict: false. TypeScript should make your life easier, not turn you into a type theorist.

Step-by-Step Migration Process That Actually Works

TypeScript Longform Logo

Phase 1: Preparation (1-2 weeks)

  1. Audit your dependencies: Run npm ls and identify packages without @types support. You'll need to write basic declarations for these. Check DefinitelyTyped first.

  2. Set up build tooling: Configure TypeScript compiler to work alongside your existing JavaScript build. Enable allowJs: true so you can mix .js and .ts files during migration. The compiler options reference explains each setting.

  3. Create migration-friendly tsconfig.json: Start with recommended tsconfig bases for your environment:

{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "allowJs": true,
    "outDir": "./dist",
    "strict": false,
    "noImplicitReturns": false,
    "skipLibCheck": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Phase 2: File Conversion (2-8 weeks)

  1. Start with utility files: Pure functions, constants, and helper modules convert easily. These usually have minimal dependencies and clear inputs/outputs.

  2. Move to data models: Convert your interfaces, API response types, and configuration objects. These provide type safety for the rest of your codebase.

  3. Tackle business logic: Convert your core application logic. This is where you'll hit most of the complex type issues.

  4. Finish with integration files: Convert files that integrate with external APIs, frameworks, or legacy systems last. These often need the most any types.

Phase 3: Type Safety Improvements (Ongoing)

  1. Enable strict checks gradually: Turn on one strict compiler option per week. Fix the errors it surfaces before enabling the next one. The strict mode guide explains each option.

  2. Replace any types systematically: Use TypeScript's noImplicitAny error reports to find files that need type improvements. Type coverage tools help track progress.

  3. Add tests for type coverage: Use tools like typescript-coverage-report to track your progress visually.

The Top 5 Errors That Kill Migrations

TypeScript Color Variants

Error 1: Cannot find module './relative-path'

What it means: TypeScript can't find your internal modules, usually because of path resolution differences.

The fix:

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

Update your imports to use the configured paths:

// Before
import Button from '../../../components/Button'
// After  
import Button from '@components/Button'

Error 2: Property 'foo' does not exist on type '{}'

What it means: TypeScript inferred an empty object type, but you're trying to access properties on it.

Quick fix: Add a type annotation:

// Broken
const user = {};
user.name = "John"; // Error!

// Fixed
const user: { name?: string } = {};
user.name = "John"; // Works

Better fix: Define proper interfaces:

interface User {
  name?: string;
  email?: string;
}

const user: User = {};
user.name = "John"; // Type-safe

Error 3: Argument of type 'string | undefined' is not assignable to parameter of type 'string'

What it means: Strict null checks caught a potential undefined value.

The fix: Handle the undefined case explicitly:

// Broken
function processName(name: string | undefined) {
  return name.toUpperCase(); // Error!
}

// Fixed
function processName(name: string | undefined) {
  if (!name) return "";
  return name.toUpperCase(); // Type-safe
}

Error 4: Cannot find module 'some-library' or its corresponding type declarations

What it means: No type definitions available for this library.

Quick fix: Create a basic declaration file:

// types/some-library.d.ts
declare module 'some-library' {
  export function someFunction(arg: any): any;
}

Better fix: Install the official types or write proper declarations:

npm install --save-dev @types/some-library

Error 5: Expected 2 arguments, but got 1

What it means: Function signature mismatch between JavaScript usage and TypeScript types.

The fix: Check if you need to make parameters optional:

// Library expects 2 args but you only pass 1
function apiCall(url: string, options?: RequestInit) {
  // Make second parameter optional with ?
}

Advanced Migration Challenges

Dealing with this Context Issues

JavaScript's dynamic this binding breaks TypeScript's type system. Convert problematic patterns:

// Problematic JavaScript pattern
const handler = {
  data: [],
  onClick: function() {
    this.data.push('item'); // `this` type is unclear
  }
};

// TypeScript-friendly pattern  
class Handler {
  private data: string[] = [];
  
  onClick = () => {
    this.data.push('item'); // `this` is properly typed
  }
}

Framework-Specific Issues

React: Event handlers need explicit types:

// Before
const handleClick = (e) => { /* ... */ }

// After
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => { /* ... */ }

Node.js: Module resolution requires explicit configuration:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true
  }
}

Using Automated Migration Tools

ts-migrate (Airbnb's Tool)

Best for large React codebases. Handles 80% of mechanical conversion but leaves you with lots of any types to clean up. The ts-migrate GitHub repo has detailed usage instructions.

npx ts-migrate-full <project-folder>

Handles React prop types conversion and renames files automatically. But it generates tons of any types and doesn't handle complex type relationships. Read Airbnb's migration story for context.

AI-Assisted Migration

Tools like GitHub Copilot can help with individual file conversions but struggle with large-scale architectural decisions.

Best practice: Use AI for converting utility functions and simple components. Handle complex business logic manually. The TypeScript conversion patterns guide shows common transformations.

Team Management During Migration

Set Clear Expectations

  • Migration will slow down feature development by 20-30% initially
  • First month is mostly about getting things compiling, not perfect types
  • Type coverage improves gradually over 3-6 months

Create Migration Guidelines

  1. No mixing migration work with features: Keep migration PRs separate from business logic changes
  2. Review process: All migration PRs need TypeScript-experienced reviewer
  3. Progress tracking: Weekly reports on files converted and error count reduction

Handle Team Resistance

  • JavaScript purists: Show them refactoring safety and IDE autocomplete benefits
  • Deadline pressure: Start migration during low-feature periods
  • Learning curve: Pair experienced TypeScript developers with JavaScript experts

The migration is worth it - every team that completes it reports better code quality, faster debugging, and more confident refactoring. But plan for the reality, not the marketing promises.

Migration Troubleshooting FAQ

Q

Our TypeScript build is 5x slower than our JavaScript build. Is this normal?

A

Yes, unfortunately. TypeScript compilation adds significant overhead, especially on first build. Large projects can take 2-5 minutes instead of 30 seconds. I've seen a React app go from 15-second builds to 4 minutes just from adding TypeScript - the type checker crawls through every import to verify types.

Immediate fixes:

  • Use skipLibCheck: true to skip type checking node_modules
  • Enable incremental compilation with incremental: true and tsc --build
  • Use transpileOnly mode in development with ts-loader or ts-node

Long-term fixes:

  • Split your monorepo using TypeScript project references
  • Consider swc or esbuild for faster builds in development
  • The upcoming Go rewrite of tsc promises 10x improvements, but it's been "coming soon" for years
Q

We have 347 TypeScript errors and counting. Should we fix them all before shipping?

A

Fuck no. Ship first, perfect later. Set up your build pipeline to treat TypeScript as a linter, not a compiler blocker.

Use // @ts-expect-error for known issues you'll fix later:

// @ts-expect-error - TODO: Fix user type definition
user.id = response.userId;

Better to ship with some type compromises than get paralyzed by perfectionism.

Q

Half our third-party libraries don't have type definitions. What now?

A

Create a types/ directory and write minimal declarations:

// types/sketchy-lib.d.ts
declare module 'sketchy-lib' {
  export function doSomething(arg: any): any;
  export default any;
}

For popular libraries, check DefinitelyTyped first:

npm install --save-dev @types/library-name

If it doesn't exist, contribute basic types back to the community. Future you will thank past you.

Q

Our Express app broke after migration - req.user is undefined but TypeScript says it exists?

A

TypeScript types disappear at runtime. Your middleware isn't actually setting req.user. This bit me hard during a migration - TypeScript was happy with req.user.id but the app threw "Cannot read property 'id' of undefined" in production because the auth middleware failed silently.

Add runtime checks:

app.post('/api/data', (req, res) => {
  if (!req.user) {
    return res.status(401).json({ error: 'Not authenticated' });
  }
  // Now req.user is guaranteed to exist
});

Or use type guards:

function isAuthenticated(req: Request): req is Request & { user: User } {
  return !!req.user;
}
Q

Our React components have 50+ TypeScript errors each. How do we fix this efficiently?

A

Start with props and work down:

  1. Define prop interfaces first:
interface ButtonProps {
  children: React.ReactNode;
  onClick: () => void;
  disabled?: boolean;
}
  1. Use ComponentProps for extending existing components:
interface CustomButtonProps extends React.ComponentProps<'button'> {
  variant: 'primary' | 'secondary';
}
  1. Convert event handlers last - they're the most annoying:
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
  e.preventDefault();
  // ...
};
Q

TypeScript is complaining about our Redux store. Do we need to rewrite everything?

A

No, but Redux + TypeScript is painful without proper setup. Use Redux Toolkit - it handles most TypeScript integration for you.

For existing Redux code, start with typing your actions:

interface SetUserAction {
  type: 'SET_USER';
  payload: User;
}

type AppAction = SetUserAction | /* other actions */;

Then type your reducers:

const userReducer = (state: UserState = initialState, action: AppAction): UserState => {
  // TypeScript now knows about all possible actions
};
Q

Our Webpack config broke after adding TypeScript. What's the minimal fix?

A

Add ts-loader to your webpack.config.js:

module.exports = {
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
};

For faster builds, use transpileOnly: true in development:

{
  test: /\.tsx?$/,
  use: [
    {
      loader: 'ts-loader',
      options: {
        transpileOnly: true, // Skip type checking in webpack
      },
    },
  ],
}

Run tsc --noEmit separately to check types.

Q

My team wants to revert back to JavaScript. How do I convince them to push through?

A

Show them concrete wins:

  1. Set up TypeScript in VS Code properly - the autocomplete is magical
  2. Do a refactoring demo - rename a function and watch TypeScript catch all usage sites
  3. Show the error prevention - demonstrate how TypeScript catches bugs before they hit production

But also acknowledge the pain:

  • Migration slows down development initially
  • Learning curve is real
  • Some JavaScript patterns don't translate cleanly

Give the team permission to use any liberally at first. Type safety can improve gradually.

Q

Should we migrate our tests to TypeScript too?

A

Start with just the application code. Test migration is optional and adds complexity.

If you do migrate tests, use looser type checking:

// tsconfig.test.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "strict": false,
    "noImplicitAny": false
  },
  "include": ["**/*.test.ts", "**/*.spec.ts"]
}

Jest with TypeScript requires ts-jest or babel-preset-typescript. Both have quirks with path resolution and module mocking.

Q

Our CI/CD pipeline fails because TypeScript is too strict. Should we relax the settings?

A

Yes, temporarily. In CI, treat TypeScript errors as warnings initially:

// tsconfig.json for CI
{
  "compilerOptions": {
    "noEmitOnError": false,  // Don't fail build on type errors
    "skipLibCheck": true    // Skip checking node_modules
  }
}

Set up a separate job that runs tsc --noEmit and reports type errors without failing the build. This lets you ship while tracking type coverage improvements.

Q

How long did other companies take to migrate? Are we behind schedule?

A

Stripe: 6 months for their main web app (~100K lines)
Slack: 8 months for their desktop app with 4 engineers
Airbnb: Built ts-migrate specifically because manual migration was taking too long

Every codebase is different. Focus on steady progress rather than speed:

  • 5-10 files per week is reasonable for a small team
  • Large files (>500 lines) count as multiple files
  • Framework integration (React, Node.js) adds significant time

You're not behind if you're making consistent progress and the codebase is improving.

Migration Tools and Resources That Actually Help

Related Tools & Recommendations

tool
Similar content

Webpack: The Build Tool You'll Love to Hate & Still Use in 2025

Explore Webpack, the JavaScript build tool. Understand its powerful features, module system, and why it remains a core part of modern web development workflows.

Webpack
/tool/webpack/overview
100%
tool
Similar content

ESLint - Find and Fix Problems in Your JavaScript Code

The pluggable linting utility for JavaScript and JSX

/tool/eslint/overview
88%
tool
Similar content

Webpack Performance Optimization: Fix Slow Builds & Bundles

Optimize Webpack performance: fix slow builds, reduce giant bundle sizes, and implement production-ready configurations. Improve app loading speed and user expe

Webpack
/tool/webpack/performance-optimization
84%
tool
Similar content

Turborepo Overview: Optimize Monorepo Builds & Caching

Finally, a build system that doesn't rebuild everything when you change one fucking line

Turborepo
/tool/turborepo/overview
63%
compare
Recommended

Framework Wars Survivor Guide: Next.js, Nuxt, SvelteKit, Remix vs Gatsby

18 months in Gatsby hell, 6 months testing everything else - here's what actually works for enterprise teams

Next.js
/compare/nextjs/nuxt/sveltekit/remix/gatsby/enterprise-team-scaling
62%
review
Recommended

Vite vs Webpack vs Turbopack: Which One Doesn't Suck?

I tested all three on 6 different projects so you don't have to suffer through webpack config hell

Vite
/review/vite-webpack-turbopack/performance-benchmark-review
61%
tool
Similar content

Deno Overview: Modern JavaScript & TypeScript Runtime

A secure runtime for JavaScript and TypeScript built on V8 and Rust

Deno
/tool/deno/overview
59%
tool
Similar content

npm Enterprise Troubleshooting: Fix Corporate IT & Dev Problems

Production failures, proxy hell, and the CI/CD problems that actually cost money

npm
/tool/npm/enterprise-troubleshooting
56%
tool
Similar content

GraphQL Overview: Why It Exists, Features & Tools Explained

Get exactly the data you need without 15 API calls and 90% useless JSON

GraphQL
/tool/graphql/overview
51%
tool
Similar content

React Production Debugging: Fix App Crashes & White Screens

Five ways React apps crash in production that'll make you question your life choices.

React
/tool/react/debugging-production-issues
49%
troubleshoot
Similar content

npm Threw ERESOLVE Errors Again? Here's What Actually Works

Skip the theory bullshit - these fixes work when npm breaks at the worst possible time

npm
/troubleshoot/npm-install-error/dependency-conflicts-resolution
47%
tool
Similar content

npm - The Package Manager Everyone Uses But Nobody Really Likes

It's slow, it breaks randomly, but it comes with Node.js so here we are

npm
/tool/npm/overview
47%
tool
Similar content

Remix & React Router v7: Solve Production Migration Issues

My React Router v7 migration broke production for 6 hours and cost us maybe 50k in lost sales

Remix
/tool/remix/production-troubleshooting
47%
tool
Similar content

tRPC Overview: Typed APIs Without GraphQL Schema Hell

Your API functions become typed frontend functions. Change something server-side, TypeScript immediately screams everywhere that breaks.

tRPC
/tool/trpc/overview
46%
tool
Similar content

Prisma ORM: TypeScript Client, Setup Guide, & Troubleshooting

Database ORM that generates types from your schema so you can't accidentally query fields that don't exist

Prisma
/tool/prisma/overview
44%
tool
Similar content

Gatsby's Decline: Slow Builds, Memory Leaks & Netlify Impact

And why you shouldn't start new projects with it in 2025

Gatsby
/tool/gatsby/overview
44%
tool
Similar content

TypeScript Overview: Catch Bugs Early with JavaScript's Type System

Microsoft's type system that catches bugs before they hit production

TypeScript
/tool/typescript/overview
44%
troubleshoot
Similar content

Fix TypeScript Module Resolution Errors: Stop 'Cannot Find Module'

Stop wasting hours on "Cannot find module" errors when everything looks fine

TypeScript
/troubleshoot/typescript-module-resolution-error/module-resolution-errors
40%
tool
Similar content

Node.js Performance Optimization: Boost App Speed & Scale

Master Node.js performance optimization techniques. Learn to speed up your V8 engine, effectively use clustering & worker threads, and scale your applications e

Node.js
/tool/node.js/performance-optimization
40%
tool
Similar content

Azure DevOps Services: Enterprise Reality, Migration & Cost

Explore Azure DevOps Services, Microsoft's answer to GitHub. Get an enterprise reality check on migration, performance, and true costs for large organizations.

Azure DevOps Services
/tool/azure-devops-services/overview
40%

Recommendations combine user behavior, content similarity, research intelligence, and SEO optimization